commit a513701afce7b03173213a2f67dfd9dd28fa1868 Author: Adeel Abbas Date: Mon Apr 9 13:51:33 2018 -0700 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..78547e5 --- /dev/null +++ b/.gitignore @@ -0,0 +1,36 @@ +.DS_Store +build/ +scripts/*.pyc + +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app diff --git a/AUTHORS.md b/AUTHORS.md new file mode 100644 index 0000000..d0f5c3a --- /dev/null +++ b/AUTHORS.md @@ -0,0 +1,9 @@ +# GPR Authors List + +##### Please add entries to the bottom of the list in the following format +* `@GitHub UserName (Required) - [Name and/or Organization](link)` + +# Authors +* @aabbas-gpfw - [GoPro, Inc.](https://github.com/GoPro/gpr) +* @dnewman-gpsw - [GoPro, Inc.](https://github.com/GoPro/gpr) + diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..ea7ab49 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,71 @@ +# minimum required cmake version +cmake_minimum_required( VERSION 3.5 FATAL_ERROR ) + +set(CMAKE_SUPPRESS_REGENERATION true) + +set(CMAKE_C_FLAGS "-std=c99") + +# project name +project( gpr ) + +if(EXISTS "${CMAKE_SOURCE_DIR}/.git") + execute_process( + COMMAND git rev-parse --abbrev-ref HEAD + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_BRANCH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) + + execute_process( + COMMAND git log -1 --format=%h + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} + OUTPUT_VARIABLE GIT_COMMIT_HASH + OUTPUT_STRIP_TRAILING_WHITESPACE + ) +else(EXISTS "${CMAKE_SOURCE_DIR}/.git") + set(GIT_BRANCH "") + set(GIT_COMMIT_HASH "") +endif(EXISTS "${CMAKE_SOURCE_DIR}/.git") + +message(STATUS "Git current branch: ${GIT_BRANCH}") +message(STATUS "Git commit hash: ${GIT_COMMIT_HASH}") + +add_definitions("-DGIT_COMMIT_HASH=\"${GIT_COMMIT_HASH}\"") +add_definitions("-DGIT_BRANCH=\"${GIT_BRANCH}\"") + +# add needed subdirectories +add_subdirectory( "source/lib/common" ) +add_subdirectory( "source/lib/vc5_common" ) + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + MESSAGE( STATUS "Found source/lib/vc5_decoder - enabling vc5 decoder and setting GPR_READING=1" ) + add_subdirectory( "source/lib/vc5_decoder" ) + add_subdirectory( "source/app/vc5_decoder_app" ) +else(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + MESSAGE( STATUS "Could not find source/lib/vc5_decoder - disabling vc5 decoder and setting GPR_READING=0" ) +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + MESSAGE( STATUS "Found source/lib/vc5_encoder - enabling vc5 encoder and setting GPR_WRITING=1" ) + add_subdirectory( "source/lib/vc5_encoder" ) + add_subdirectory( "source/app/vc5_encoder_app" ) +else(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + MESSAGE( STATUS "Could not find source/lib/vc5_encoder - disabling vc5 encoder and setting GPR_WRITING=0" ) +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + +add_subdirectory( "source/lib/dng_sdk" ) +add_subdirectory( "source/lib/gpr_sdk" ) +add_subdirectory( "source/lib/xmp_core" ) +add_subdirectory( "source/lib/expat_lib" ) +add_subdirectory( "source/lib/md5_lib" ) + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + MESSAGE( STATUS "Found source/lib/tiny_jpeg - enabling jpeg writing and setting GPR_JPEG_AVAILABLE=1" ) + add_subdirectory( "source/lib/tiny_jpeg" ) +else(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + MESSAGE( STATUS "Could not find source/lib/tiny_jpeg - disabling jpeg writing and setting GPR_JPEG_AVAILABLE=0" ) +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + +add_subdirectory( "source/app/common/cJSON" ) +add_subdirectory( "source/app/common/argument_parser" ) +add_subdirectory( "source/app/gpr_tools" ) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..8e06168 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# How to Contribute + +Want to contribute to GPR? Please take a moment to review this document in order to make your contribution and the process around it easier and more effective for everyone. + +## Issue tracker + +The [issue tracker](https://www.github.com/gopro/gpr/issues) is the best place to report a bug. Please make sure to check the following guide on how to use the issue tracker : + +### Reporting a bug + +- If you really think it is a bug, consult the list of issues and make sure nobody has reported it yet. This will avoid duplication of effort. +- If it hasn't been reported yet, submit a new issue. + +### Suggesting a feature + +- Consult the roadmap within [projects](https://github.com/gopro/gpr/projects) to know if it is planned. +- Consult the list of things that **won't** be implemented. +- Read up on what type of feature requests are accepted. +- See if anybody has not requested the feature yet. +- If it hasn't beed requested yet, submit a new request as an issue. + +### Submitting a pull-request + +- All contributors must sign a [contributor license agreement (CLA)](https://cla.gopro.com). Your code will not be reviewed and accepted by the Admins until this has been received. +- To be sure your changes could be interesting and accepted, ask about your patch on Discourse. +- Fork the repository and work on the `master` branch, while respecting the imposed guidelines. +- Add tests if needed, and make sure that all of them pass. +- Document the code according to the guidelines, and make sure the build is OK. If you encounter some problem with the Closure Compiler part, don't hesitate to ask. +- Submit a pull request for your changes. +- The Admins will review your work and may optionally request conformance, functional or other changes. Work with them to resolve any issues. +- Upon acceptance, your code will be added to the main branch and available for all. diff --git a/LICENSE-APACHE b/LICENSE-APACHE new file mode 100644 index 0000000..3d928da --- /dev/null +++ b/LICENSE-APACHE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 GoPro, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/LICENSE-MIT b/LICENSE-MIT new file mode 100644 index 0000000..a32b051 --- /dev/null +++ b/LICENSE-MIT @@ -0,0 +1,19 @@ +Copyright 2018 GoPro, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the "Software"), +to deal in the Software without restriction, including without limitation +the rights to use, copy, modify, merge, publish, distribute, sublicense, +and/or sell copies of the Software, and to permit persons to whom the +Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..45b469d --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,228 @@ +Apache License 2.0 or MIT + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright 2018 GoPro, Inc. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +OR + + Copyright 2018 GoPro, Inc. + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + diff --git a/README.md b/README.md new file mode 100755 index 0000000..a6dd1a7 --- /dev/null +++ b/README.md @@ -0,0 +1,305 @@ +# GPR Introduction + +The General Purpose Raw (GPR) is 12-bit raw image coding format that is based on [Adobe DNG®](https://helpx.adobe.com/photoshop/digital-negative.html) standard. Image compression is a balance of speed, file size and photo quality, and typically one can only choose two. GPR was designed to provide a better tradeoff for all three parameters than what's possible with DNG or any other raw format. The intention of GPR is not to compete with DNG, rather to be as close as possible to DNG. This guarantees compatibility with applications that already understand DNG, but provide an alternate compression scheme in situations where compression and encoding/decoding speed matter. + +Action cameras, like that from GoPro, have limited computing resources, so ability to compress data using fewest CPU cycles matters. File sizes matter because GoPro cameras can record thousands of images very quickly using timelapse and burst mode features. As the world shifts from desktop to mobile, people now shoot and process more and more photos on smartphones which are always limited on storage space and bandwidth. And last but not the least, image quality matters because we want GPR to provide visually transparent image quality when compared to uncompressed DNG. All this combined enables customers to capture DSLR-class image quality in a GPR file that has nearly same size as JPEG, on a camera that is as small and rugged as a GoPro. + +DNG allows storage of RAW sensor data in three main formats: uncompressed, lossless JPEG or lossy JPEG. Lossless mode typically achieves 2:1 compression that is clearly not enough in the mobile-first age. Lossy mode uses the 8x8 DCT transform that was developed for JPEG more than 25 years ago (when photo resolutions were much smaller), achieving compression ratios around 4:1. In comparison, GPR achieves typical compression ratios between 10:1 and 4:1. This is due to Full-Frame Wavelet Transform (FFWT). FFWT has a few nice properties compared to DCT: + - The compression performance increases as image resolutions go up - making it more future proof + - Better image quality as it does not suffer from ringing or blocky artifacts observed in JPEG files. + +The wavelet codec in GPR is not new, but has been a SMPTE® standard under the name [VC-5](https://kws.smpte.org/higherlogic/ws/public/projects/15/details). VC-5 shares a lot of technical barebones with the [CineForm®](https://gopro.github.io/cineform-sdk/), an open and cross-platform intermediate codec designed for high-resolution video editing. + +## File Types + +Following file types are discussed in this document: + +* `RAW or CFA RAW` - The Bayer RAW format is typically composed of 50% green, 25% red and 25% blue samples captured from sensor, e.g. RGGB and GBRG. The RAW image doesn't directly carry metadata about image development e.g. exposure, white balance or noise etc, thus cannot easily be turned into a well developed image. + +* `DNG` - Widely regarded as a defacto standard, a DNG file can be opened natively on most operating systems and image development tools. As mentioned earlier, DNG stores compressed or uncompressed RAW sensor data along with accompanying metadata that is needed to properly develop image. + +* `GPR` - General purpose RAW format file. GoPro cameras including Hero5/6 and Fusion record photos in this format. GPR is an extension of DNG, enabling high performance VC-5 compression for faster storage and smaller files without impacting image quality. Today, GPR files can be opened in Adobe products like Camera Raw®, Photoshop® and Lightroom®. + +* `VC5` - A file with VC5 extension stores RAW sensor image data in a compressed format that is compatible with VC-5. Similar to RAW file, VC5 file does not store metadata needed for proper image development. + +* `PPM` - [Portable Pixel Map](http://netpbm.sourceforge.net/doc/ppm.html) is one of the simplest storage formats of uncompressed debayered RGB image. It is very easy to write and analyze programs to process this format, and that is why it is used here. + +* `JPG or JPEG` One of the simplest formats for lossy compression of debayered RGB image. + +# Included Within This Repository + +* The complete source of GPR-SDK, a library that implements conversion of RAW, DNG, GPR, PPM or JPG formats. GPR-SDK has C-99 interface for maximum portability, yet is implemented in C/C++ for programming effectiveness. + +* Source of VC-5 encoder and decoder library. Encoder is hand-optimized with NEON intrinsics for ARM processors. + +* `gpr_tools` - a sample demo code that uses GPR-SDK to convert between GPR, DNG and RAW formats. + +* `vc5_encoder_app` - a sample VC5 encoder application that encodes a RAW frame to VC5 file. + +* `vc5_decoder_app` - a sample VC5 decoder application that decodes VC5 file to RAW file. + +* CMake support for building all projects. + +* Tested on: + - macOS High Sierra with XCode v8 & v9, El Capitan with XCode v8 + - Windows 10 with Visual Studio 2015 & 2017 + - Ubuntu 16.04 with gcc v5.4 + +# License Terms + +GPR is licensed under either: + +* Apache License, Version 2.0, (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0) +* MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT) + +at your option. + +## Contribution + +Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. + +# Quick Start for Developers + +## Setup Source Code + +Clone the project from Github (`git clone https://github.com/gopro/gpr`). You will need [CMake](https://cmake.org/download/) version 3.5.1 or better to compile source code. + +### Compiling Source Code + +Run following commands: +``` +$ mkdir build +$ cd build +$ cmake ../ +``` + +For Xcode, use command line switch `-G Xcode`. On Windows, CMake should automatically figure out installed version of Visual Studio and generate corresponding project files. + +## Using gpr_tools + +Some example commands are shown below: + +Convert GPR to DNG: + +``` +$ gpr_tools -i INPUT.GPR -o OUTPUT.DNG +``` + +Extract RAW from GPR: + +``` +$ gpr_tools -i INPUT.GPR -o OUTPUT.RAW +``` + +Convert DNG to GPR: + +``` +$ gpr_tools -i INPUT.DNG -o OUTPUT.GPR +``` + +Analyze a GPR (or even DNG) and output parameters that define DNG metadata to a file: + +``` +$ gpr_tools -i INPUT.GPR -d 1 > PARAMETERS.TXT +``` + +Read RAW pixel data, along with parameters that define DNG metadata and apply to an output GPR (or DNG) file: + +``` +$ gpr_tools -i INPUT.RAW -o OUTPUT.DNG -a PARAMETERS.TXT +``` + +Read GPR file and output PPM preview: + +``` +$ gpr_tools -i INPUT.GPR -o OUTPUT.PPM +``` + +Read GPR file and output JPG preview: + +``` +$ gpr_tools -i INPUT.GPR -o OUTPUT.JPG +``` + +For a complete list of commands, please refer to data/tests/run_tests.sh + +## Using vc5_encoder_app + +vc5_encoder_app is an optional tool that can be used to convert RAW image data to VC5 essence, as shown below: + +``` +$ vc5_encoder_app -i INPUT.RAW -o OUTPUT.VC5 -w 4000 -h 3000 -p 8000 +``` + +## Using vc5_decoder_app + +vc5_decoder_app is an optional tool that can be used to decode VC5 essence into RAW image data, as shown below: + +``` +$ vc5_decoder_app -i INPUT.VC5 -o OUTPUT.RAW +``` + +## Source code organization + +### Folder structure + +Source code is organized inside `source` folder. All library and sdk code is located in `lib` folder, while all tools and applications that use `lib` are located in `app` folder. + +The ``` lib``` folder is made up of following folders: + +- `common` - common source code that is accessable to all libraries and applications +- `dng_sdk` - mostly borrowed from [Adobe DNG SDK](https://www.adobe.com/support/downloads/dng/dng_sdk.html) version 1.4. +- `xmp_core` - [Extensible Metadata Platform](https://en.wikipedia.org/wiki/Extensible_Metadata_Platform), mostly borrowed from Adobe DNG SDK version 1.4 +- `expat_lib` - A stream-oriented XML parser borrowed from Adobe DNG SDK version 1.4 +- `vc5_decoder` - vc5 decoder. If this is not present, cmake won't use it (and define `GPR_READING=0`); otherwise cmake uses it (and defines `GPR_READING=1`). +- `vc5_encoder` - vc5 encoder. If this is not present, cmake won't use it (and define `GPR_WRITING=0`); otherwise cmake uses it (and defines `GPR_WRITING=1`). +- `vc5_common` - common source code that is shared between vc5_encoder and vc5_decoder +- `md5_lib` - md5 checksum calculation library +- `tiny_jpeg` - lightweight jpeg encoder available [here](https://github.com/serge-rgb/TinyJPEG). If this folder is not present, cmake will not use it (and define `GPR_JPEG_AVAILABLE=0`); otherwise cmake defines `GPR_JPEG_AVAILABLE=1` and uses it. +- `gpr_sdk` - uses all modules above to read/write GPR files. + +The `app` folder is made up of following folders: + +- `common` - common application level source code that is accessable to sample applications +- `vc5_decoder_app` - sample vc5 decoder application +- `vc5_encoder_app` - sample vc5 encoder application +- `gpr_tools` - utility to convert to/from various formats mentioned above and measure runtime + +### Important Defines + +Here are some important compile time definitions: + +* ```GPR_TIMING``` enables timing code that prints out time spent (in milliseconds) in different functions. In production builds, applications should define ```TIMING=0```. Set to a higher value to output greater timing information. + +* ```GPR_WRITING``` enables all code that writes GPR files. If application does not need to write GPR, set ```GPR_WRITING=0``` to reduce code size. + +* ```GPR_READING``` enables all code that reads GPR files. If application does not need to read GPR, set ```GPR_READING=0``` to reduce code size. + +* ```GPR_JPEG_AVAILABLE``` enables lightweight jpeg encoder located in `source/lib/tiny_jpeg`. This is used to write a small thumbnail inside GPR file. If ```GPR_JPEG_AVAILABLE=0```, thumnail is not written, although you can still set pre-encoded jpeg file as thumbnail, by using -P, -W and -H command line options in `gpr_tools`. + +* ```NEON``` enables arm neon intrinsics (disabled by default). This can be enabled from CMake by ```-DNEON=1``` switch. + +### GPR-SDK API + +GPR-SDK API is defined in header files in the following folders: + +- source/lib/common/public +- source/lib/gpr_sdk/public + +An application needs to include above two folders in order to access API. API defines various functions named ```gpr_convert_XXX_to_XXX``` which convert from one format to another. As an example, GPR to DNG conversion is done from ```gpr_convert_gpr_to_dng```. When output file is GPR or DNR, ```gpr_parameters``` structure has to be specified. Fields in this structure map to DNG metadata tags, and we have tried to abstract low-level DNG details in a very clean and easy to use structure. + +# Compression Technology + +## Wavelet Transforms +The wavelet used within VC-5 is a 2D three-level 2-6 Wavelet. If you look up wavelets on Wikipedia, prepare to get confused fast. Wavelet compression of images is fairly simple if you don't get distracted by the theory. The wavelet is a one dimensional filter that separates low frequency data from high frequency data, and the math is simple. For each two pixels in an image simply add them (low frequency): +* low frequency sample = pixel[x] + pixel[x+1] +- two inputs, is the '2' part of 2-6 Wavelet. + +For high frequency it can be as simple as the difference of the same two pixels: +* high frequency sample = pixel[x] - pixel[x+1] +- this would be a 2-2 Wavelet, also called a [HAAR wavelet](https://en.wikipedia.org/wiki/Haar_wavelet). + +For a 2-6 wavelet this math is for the high frequency: +* high frequency sample = pixel[x] - pixel[x+1] + (-pixel[x-2] - pixel[x-1] + pixel[x+2] + pixel[x+3])/8 +- i.e 6 inputs for the high frequency, the '6' part of 2-6 Wavelet. + +The math doesn't get much more complex than that. + +To wavelet compress a monochrome frame (color can be compressed as separate monochrome channels), we start with a 2D array of pixels (a.k.a image.) + +![](data/readmegfx/source-640.png "Source image") + +If you store data with low frequencies (low pass) on the left and the high frequencies (high pass) on the right you get the image below. A low pass image is basically the average, and high pass image is like an edge enhance. + +![](data/readmegfx/level1D-640.png "1D Wavelet") + +You repeat the same operation vertically using the previous output as the input image. + +Resulting in a 1 level 2D wavelet: + +![](data/readmegfx/level1-640.png "1D Wavelet") + +For a two level wavelet, you repeat the same horizontal and vertical wavelet operations of the top left quadrant to provide: + +![](data/readmegfx/level2-640.png "2 Level 2D-Wavelet") + +Repeating again for the third level. + +![](data/readmegfx/level3-640.png "3 Level 2D-Wavelet") + +## Quantization + +All that grey is easy to compress. The reason there is very little information in these high frequency regions is that the high frequency data of the image has been quantized. The human eye is not very good at seeing subtle changes in high frequency regions, so this is exploited by scaling the high-frequency samples before they are stored: + +* high frequency sample = (wavelet output) / quantizer + +## Entropy Coding + +After the wavelet and quantization stages, you have the same number of samples as the original source. The compression is achieved as the samples are no longer evenly distributed (after wavelet and quantization.) There are many many zeros and ones, than higher values, so we can store all these values more efficiently, often up to 10 times more so. + +### Run length + +The output of the quantization stage has a lot of zeros, and many in a row. Additional compression is achieved by counting runs of zeros, and storing them like: a "z15" for 15 zeros, rather than "0,0,0,0,0,0,0,0,0,0,0,0,0,0,0" + +### Variable length coding + +After all previous steps, the high frequency samples are stored with a variable length coding scheme using [Huffman coding](https://en.wikipedia.org/wiki/Huffman_coding). A table then maps sample values to codewords with differing bit lengths where most common codewords are expressed in few bits and rare codewords are expressed in larger bits. + +The lack of complexity is what makes VC-5 fast. Low pass filter is just addition. High pass filter is 6 tap where all coefficients are rational numbers and no multiplication or division is required. Variable length coding can be implemented with a lookup table, an approach that is faster than other entropy coding techniques. + +## To Decode + +Reverse all the steps. + +# Thumbnail and Preview Generation + +A nice property of the Wavelet codec is scalability support: i.e. various resolutions ranging from original coded resolution to one-sixteenth resolution are encoded and can be retrieved efficiently. Scalability means that extracting lowest resolution is fastest and cost of extracting resolutions increases as resolution goes up. For application scenarios where rendering a smaller resolution suffices, a decoder can very cheaply extract lower resolution. Common examples of this use case are thumbnail previews in file browsers or rendering image on devices with smaller resolution e.g. mobile phones. + +Scalability is more efficient than decoding full resolution image, performing demosaicing and downsampling. To avoid demosaic, DNG allows mechanism to store a separate thumbnail and preview image (often encoded in JPG). File browers use thumbnail, while preview is useful for rendering higher resolution version of image. Since these are separately enoded images and do not exploit compression amongst each other or with original RAW image, file sizes add up quickly. + +As an example, GoPro Hero6 Black captures 4000x3000 RAW image in Bayer RGGB format. Red, blue and two green channels are split up and separately encoded into wavelet resolutions of 2000x1500 (or 2:1), 1000x750 (or 4:1), 500x375 (or 8:1). The Low-Low band of lowest resolution wavelet is a 250x188 (or 16:1) image, and it is stored uncompressed (generating thumbnail is essentially a memory copy). RGB images at other resolutions can be obtained at successive complexity levels, without performing demosaicing. To illustrate this, decoding speed of various resolutions is measured and shown using `gpr_tools` (in milliseconds inside square brackets). + +``` +Decode GPR to 8-bit PPM (250x188) +[ 6-ms] [BEG] gpr_convert_gpr_to_rgb() gpr.cpp (line 1695) +[ 16-ms] [END] gpr_convert_gpr_to_rgb() gpr.cpp (line 1738) +``` + +``` +Decode GPR to 8-bit PPM (500x375) +[ 6-ms] [BEG] gpr_convert_gpr_to_rgb() gpr.cpp (line 1695) +[ 37-ms] [END] gpr_convert_gpr_to_rgb() gpr.cpp (line 1738) +``` + +``` +Decode GPR to 8-bit PPM (1000x750) +[ 5-ms] [BEG] gpr_convert_gpr_to_rgb() gpr.cpp (line 1695) +[ 130-ms] [END] gpr_convert_gpr_to_rgb() gpr.cpp (line 1738) +``` + +``` +Decode GPR to 8-bit PPM (2000x1500) +[ 5-ms] [BEG] gpr_convert_gpr_to_rgb() gpr.cpp (line 1695) +[ 357-ms] [END] gpr_convert_gpr_to_rgb() gpr.cpp (line 1738) +``` + +And here is the output of full GPR to DNG decoding. + +``` +Decode GPR to DNG +[ 6-ms] [BEG] gpr_convert_gpr_to_dng() gpr.cpp (line 1748) +[ 422-ms] [END] gpr_convert_gpr_to_dng() gpr.cpp (line 1768) +``` + +To summarize, here are speed gain factors over full resolution DNG decoding: + +| Resolution | 250x188 | 500x375 | 1000x750 | 2000x1500 +| :---: | :---: | :---: | :---: | :---: +| Speed factor | 41.6x | 13.4x | 3.3x | 1.2x + +Demosaicing has a higher complexity than GPR decoding, so numbers for RGB output after demosaic will be higher. Similar speed improvements can also be seen when writing JPG file. + +``` +GoPro and CineForm are trademarks of GoPro, Inc. +DNG, Photoshop and Lightroom is trademarks of Adobe Inc. +``` diff --git a/data/readmegfx/level1-640.png b/data/readmegfx/level1-640.png new file mode 100644 index 0000000..eea1a79 Binary files /dev/null and b/data/readmegfx/level1-640.png differ diff --git a/data/readmegfx/level1D-640.png b/data/readmegfx/level1D-640.png new file mode 100644 index 0000000..bc661d1 Binary files /dev/null and b/data/readmegfx/level1D-640.png differ diff --git a/data/readmegfx/level2-640.png b/data/readmegfx/level2-640.png new file mode 100644 index 0000000..c12b1d7 Binary files /dev/null and b/data/readmegfx/level2-640.png differ diff --git a/data/readmegfx/level3-640.png b/data/readmegfx/level3-640.png new file mode 100644 index 0000000..236ea81 Binary files /dev/null and b/data/readmegfx/level3-640.png differ diff --git a/data/readmegfx/source-640.png b/data/readmegfx/source-640.png new file mode 100644 index 0000000..e237b1c Binary files /dev/null and b/data/readmegfx/source-640.png differ diff --git a/data/samples/Fusion/GPBK7066.GPR b/data/samples/Fusion/GPBK7066.GPR new file mode 100755 index 0000000..f95c3e6 Binary files /dev/null and b/data/samples/Fusion/GPBK7066.GPR differ diff --git a/data/samples/Fusion/GPFR7066.GPR b/data/samples/Fusion/GPFR7066.GPR new file mode 100755 index 0000000..0d010a6 Binary files /dev/null and b/data/samples/Fusion/GPFR7066.GPR differ diff --git a/data/samples/Hero6/GOPR0024.GPR b/data/samples/Hero6/GOPR0024.GPR new file mode 100755 index 0000000..26d4bb5 Binary files /dev/null and b/data/samples/Hero6/GOPR0024.GPR differ diff --git a/data/tests/run_tests.sh b/data/tests/run_tests.sh new file mode 100755 index 0000000..52be64b --- /dev/null +++ b/data/tests/run_tests.sh @@ -0,0 +1,89 @@ +#!/bin/sh + +GPR_TOOLS=$1 + +# cp ../../xcode/source/app/gpr_tools/Release/gpr_tools . + +# Start with one GPR file + +rm *.GPR +rm *.DNG +rm *.PPM +rm *.JPG + +rm -rf JPB +rm -rf PPM +rm -rf DNG +rm -rf GPR +rm -rf RAW + +echo +echo Dump GPR parameters to a file +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -d 1 > GOPR0024.TXT + + +mkdir PPM +echo +echo Decode "GPR to 8-bit PPM (250x188)" +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o PPM/GOPR0024-250x188-8-bit.PPM -r 16:1 +echo +echo Decode "GPR to 8-bit PPM (500x375)" +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o PPM/GOPR0024-500x375-8-bit.PPM -r 8:1 +echo +echo Decode "GPR to 8-bit PPM (1000x750)" +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o PPM/GOPR0024-1000x750-8-bit.PPM -r 4:1 +echo +echo "Decode GPR to 8-bit PPM (2000x1500)" +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o PPM/GOPR0024-2000x1500-8-bit.PPM -r 2:1 +echo +echo Decode "GPR to 16-bit PPM (250x188)" +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o PPM/GOPR0024-250x188-16-bit.PPM -r 16:1 -b 16 +echo +echo Decode "GPR to 16-bit PPM (500x375)" +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o PPM/GOPR0024-500x375-16-bit.PPM -r 8:1 -b 16 +echo +echo Decode "GPR to 16-bit PPM (1000x750)" +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o PPM/GOPR0024-1000x750-16-bit.PPM -r 4:1 -b 16 +echo +echo "Decode GPR to 16-bit PPM (2000x1500)" +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o PPM/GOPR0024-2000x1500-16-bit.PPM -r 2:1 -b 16 + +mkdir JPG +echo +echo Decode "GPR to JPG (250x188)" +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o JPG/GOPR0024-250x188.JPG -r 16:1 +echo +echo Decode "GPR to JPG (500x375)" +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o JPG/GOPR0024-500x375.JPG -r 8:1 +echo +echo Decode "GPR to JPG (1000x750)" +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o JPG/GOPR0024-1000x750.JPG -r 4:1 +echo +echo Decode "GPR to JPG (2000x1500)" +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o JPG/GOPR0024-2000x1500.JPG -r 2:1 + +mkdir RAW +echo +echo Decode GPR to RAW +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o RAW/GOPR0024.RAW + + +mkdir DNG +echo +echo Decode GPR to DNG +$GPR_TOOLS -i ../samples/Hero6/GOPR0024.GPR -o DNG/GOPR0024.DNG +echo +echo Encode RAW to DNG +$GPR_TOOLS -i RAW/GOPR0024.RAW -o DNG/GOPR0024-FROM-RAW.DNG -a GOPR0024.TXT + +mkdir GPR +echo +echo Encode DNG to GPR and using thumbnail from JPG file that was previously written +$GPR_TOOLS -i DNG/GOPR0024.DNG -o GPR/GOPR0024-PREVIEW-250x188.GPR -P JPG/GOPR0024-250x188.JPG -H 188 -W 250 +$GPR_TOOLS -i DNG/GOPR0024.DNG -o GPR/GOPR0024-PREVIEW-500x375.GPR -P JPG/GOPR0024-500x375.JPG -H 375 -W 500 +$GPR_TOOLS -i DNG/GOPR0024.DNG -o GPR/GOPR0024-PREVIEW-1000x750.GPR -P JPG/GOPR0024-1000x750.JPG -H 750 -W 1000 +$GPR_TOOLS -i DNG/GOPR0024.DNG -o GPR/GOPR0024-PREVIEW-2000x1500.GPR -P JPG/GOPR0024-2000x1500.JPG -H 750 -W 1000 + +echo +echo Encode RAW to GPR +$GPR_TOOLS -i RAW/GOPR0024.RAW -o GPR/GOPR0024-FROM-RAW.GPR -a GOPR0024.TXT diff --git a/source/app/common/argument_parser/CMakeLists.txt b/source/app/common/argument_parser/CMakeLists.txt new file mode 100644 index 0000000..e965221 --- /dev/null +++ b/source/app/common/argument_parser/CMakeLists.txt @@ -0,0 +1,16 @@ +# library +set( LIB_NAME argument_parser ) + +# get source files +file( GLOB SRC_FILES "*.c" "*.cpp" ) + +# get include files +file( GLOB INC_FILES "*.h" ) + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/source/app/common/argument_parser/argument_parser.cpp b/source/app/common/argument_parser/argument_parser.cpp new file mode 100755 index 0000000..0e8f6fa --- /dev/null +++ b/source/app/common/argument_parser/argument_parser.cpp @@ -0,0 +1,126 @@ +/*! @file argument_parser.cpp + * + * @brief Implement class to handle argument parsing + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "argument_parser.h" + +#include + +using namespace std; + +#ifdef __GNUC__ +#define COMPILER "[GCC %d.%d.%d]", __GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__ +#elif __INTEL_COMPILER +#define COMPILER "[ICC %d]", __INTEL_COMPILER +#elif _MSC_VER +#define COMPILER "[VS %d]", _MSC_VER +#else +#define COMPILER "[Unknown-CXX]" +#endif + +#ifdef _WIN32 +#define OPERATING_SYSTEM "[Windows]" +#elif __linux +#define OPERATING_SYSTEM "[Linux]" +#elif __CYGWIN__ +#define OPERATING_SYSTEM "[Cygwin]" +#elif __APPLE__ +#define OPERATING_SYSTEM "[Mac OS X]" +#else +#define OPERATING_SYSTEM "[Unknown-OS]" +#endif + +#define NUMBER_OF_BITS "[%d bit] ", (sizeof(void*) == 8 ? 64 : 32) ///< used for checking 64-bit O/S + +argument_parser::argument_parser(bool verbose) +{ +} + +void argument_parser::set_options() +{ +} + +int argument_parser::parse(int argc, char *argv [], const char* application_text, const char* prefix_text) +{ + application_path = argv[0]; + argument_count = argc; + + for (int i = 0; i < argc; i++) + arguments[i] = argv[i]; + + set_options(); + + program_options_lite::setDefaults(command_options); + + const list& argv_unhandled = program_options_lite::scanArgv(command_options, argument_count, (const char**) arguments); + + for (list::const_iterator it = argv_unhandled.begin(); it != argv_unhandled.end(); it++) + { + fprintf(stderr, "Unhandled argument ignored: `%s'\n", *it); + } + + bool show_help = get_argument_count() == 1 || get_help(); + + if( get_verbose() || show_help ) + { + if( application_text ) + { + fprintf( stderr, "%s", application_text ); + fprintf( stderr, OPERATING_SYSTEM ); + fprintf( stderr, COMPILER ); + fprintf( stderr, NUMBER_OF_BITS ); + fprintf( stderr, "\n" ); + } + + printf("Executable: %s \n", get_application_path() ); + printf("Arguments: "); + + for (int i = 1; i < get_argument_count(); i++) + { + printf("%s ", get_argument(i)); + } + + printf("\n"); + } + + if ( show_help ) + { + print_help(); + return -1; + } + + if( application_text ) + { + if( prefix_text ) + fprintf( stderr, "%s %s", prefix_text, application_text ); + else + fprintf( stderr, "%s", application_text ); + + fprintf( stderr, OPERATING_SYSTEM ); + fprintf( stderr, COMPILER ); + fprintf( stderr, NUMBER_OF_BITS ); + fprintf( stderr, "\n" ); + } + + return 0; +} + +void argument_parser::print_help() +{ + doHelp(cout, command_options); +} + diff --git a/source/app/common/argument_parser/argument_parser.h b/source/app/common/argument_parser/argument_parser.h new file mode 100755 index 0000000..bc08a4c --- /dev/null +++ b/source/app/common/argument_parser/argument_parser.h @@ -0,0 +1,51 @@ +/*! @file argument_parser.h + * + * @brief Declare class to handle argument parsing + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define MAX_ARGC 100 + +#include "program_options_lite.h" + +class argument_parser +{ +private: + char* application_path; + int argument_count; + char* arguments[MAX_ARGC]; + +protected: + program_options_lite::Options command_options; + +public: + argument_parser(bool verbose = true); + + const int get_argument_count() { return argument_count; } + const char* get_argument(int index) { return arguments[index]; } + + const char* get_application_path() { return application_path; } + + virtual int parse(int argc, char *argv [], const char* application_text = NULL, const char* prefix_text = NULL ); + + virtual void set_options(); + + virtual void print_help(); + + virtual bool get_verbose() { return false; } + + virtual bool get_help() { return false; } + +}; diff --git a/source/app/common/argument_parser/program_options_lite.cpp b/source/app/common/argument_parser/program_options_lite.cpp new file mode 100755 index 0000000..ec3fbc5 --- /dev/null +++ b/source/app/common/argument_parser/program_options_lite.cpp @@ -0,0 +1,491 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2015, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include "program_options_lite.h" + +using namespace std; + +namespace program_options_lite +{ + + Options::~Options() + { + for(Options::NamesPtrList::iterator it = opt_list.begin(); it != opt_list.end(); it++) + { + delete *it; + } + } + + void Options::addOption(OptionBase *opt) + { + Names* names = new Names(); + names->opt = opt; + string& opt_string = opt->opt_string; + + size_t opt_start = 0; + for (size_t opt_end = 0; opt_end != string::npos;) + { + opt_end = opt_string.find_first_of(',', opt_start); + bool force_short = 0; + if (opt_string[opt_start] == '-') + { + opt_start++; + force_short = 1; + } + string opt_name = opt_string.substr(opt_start, opt_end - opt_start); + if (force_short || opt_name.size() == 1) + { + names->opt_short.push_back(opt_name); + opt_short_map[opt_name].push_back(names); + } + else + { + names->opt_long.push_back(opt_name); + opt_long_map[opt_name].push_back(names); + } + opt_start += opt_end + 1; + } + opt_list.push_back(names); + } + + /* Helper method to initiate adding options to Options */ + OptionSpecific Options::addOptions() + { + return OptionSpecific(*this); + } + + static void setOptions(Options::NamesPtrList& opt_list, const string& value) + { + /* multiple options may be registered for the same name: + * allow each to parse value */ + for (Options::NamesPtrList::iterator it = opt_list.begin(); it != opt_list.end(); ++it) + { + (*it)->opt->parse(value); + } + } + + static const char spaces[41] = " "; + + /* format help text for a single option: + * using the formatting: "-x, --long", + * if a short/long option isn't specified, it is not printed + */ + static void doHelpOpt(ostream& out, const Options::Names& entry, unsigned pad_short = 0) + { + pad_short = min(pad_short, 8u); + + if (!entry.opt_short.empty()) + { + unsigned pad = max((int)pad_short - (int)entry.opt_short.front().size(), 0); + out << "-" << entry.opt_short.front(); + if (!entry.opt_long.empty()) + { + out << ", "; + } + out << &(spaces[40 - pad]); + } + else + { + out << " "; + out << &(spaces[40 - pad_short]); + } + + if (!entry.opt_long.empty()) + { + out << "--" << entry.opt_long.front(); + } + } + + /* format the help text */ + void doHelp(ostream& out, Options& opts, unsigned columns) + { + const unsigned pad_short = 3; + /* first pass: work out the longest option name */ + unsigned max_width = 0; + for(Options::NamesPtrList::iterator it = opts.opt_list.begin(); it != opts.opt_list.end(); it++) + { + ostringstream line(ios_base::out); + doHelpOpt(line, **it, pad_short); + max_width = max(max_width, (unsigned) line.tellp()); + } + + unsigned opt_width = min(max_width+2, 28u + pad_short) + 2; + unsigned desc_width = columns - opt_width; + + /* second pass: write out formatted option and help text. + * - align start of help text to start at opt_width + * - if the option text is longer than opt_width, place the help + * text at opt_width on the next line. + */ + for(Options::NamesPtrList::iterator it = opts.opt_list.begin(); it != opts.opt_list.end(); it++) + { + ostringstream line(ios_base::out); + line << " "; + doHelpOpt(line, **it, pad_short); + + const string& opt_desc = (*it)->opt->opt_desc; + if (opt_desc.empty()) + { + /* no help text: output option, skip further processing */ + cout << line.str() << endl; + continue; + } + size_t currlength = size_t(line.tellp()); + if (currlength > opt_width) + { + /* if option text is too long (and would collide with the + * help text, split onto next line */ + line << endl; + currlength = 0; + } + /* split up the help text, taking into account new lines, + * (add opt_width of padding to each new line) */ + for (size_t newline_pos = 0, cur_pos = 0; cur_pos != string::npos; currlength = 0) + { + /* print any required padding space for vertical alignment */ + line << &(spaces[40 - opt_width + currlength]); + newline_pos = opt_desc.find_first_of('\n', newline_pos); + if (newline_pos != string::npos) + { + /* newline found, print substring (newline needn't be stripped) */ + newline_pos++; + line << opt_desc.substr(cur_pos, newline_pos - cur_pos); + cur_pos = newline_pos; + continue; + } + if (cur_pos + desc_width > opt_desc.size()) + { + /* no need to wrap text, remainder is less than avaliable width */ + line << opt_desc.substr(cur_pos); + break; + } + /* find a suitable point to split text (avoid spliting in middle of word) */ + size_t split_pos = opt_desc.find_last_of(' ', cur_pos + desc_width); + if (split_pos != string::npos) + { + /* eat up multiple space characters */ + split_pos = opt_desc.find_last_not_of(' ', split_pos) + 1; + } + + /* bad split if no suitable space to split at. fall back to width */ + bool bad_split = split_pos == string::npos || split_pos <= cur_pos; + if (bad_split) + { + split_pos = cur_pos + desc_width; + } + line << opt_desc.substr(cur_pos, split_pos - cur_pos); + + /* eat up any space for the start of the next line */ + if (!bad_split) + { + split_pos = opt_desc.find_first_not_of(' ', split_pos); + } + cur_pos = newline_pos = split_pos; + + if (cur_pos >= opt_desc.size()) + { + break; + } + line << endl; + } + + cout << line.str() << endl; + } + } + + bool storePair(Options& opts, bool allow_long, bool allow_short, const string& name, const string& value) + { + bool found = false; + Options::NamesMap::iterator opt_it; + if (allow_long) + { + opt_it = opts.opt_long_map.find(name); + if (opt_it != opts.opt_long_map.end()) + { + found = true; + } + } + + /* check for the short list */ + if (allow_short && !(found && allow_long)) + { + opt_it = opts.opt_short_map.find(name); + if (opt_it != opts.opt_short_map.end()) + { + found = true; + } + } + + if (!found) + { + /* not found */ + cerr << "Unknown option: `" << name << "' (value:`" << value << "')" << endl; + return false; + } + + setOptions((*opt_it).second, value); + return true; + } + + bool storePair(Options& opts, const string& name, const string& value) + { + return storePair(opts, true, true, name, value); + } + + /** + * returns number of extra arguments consumed + */ + unsigned parseGNU(Options& opts, unsigned argc, const char* argv[]) + { + /* gnu style long options can take the forms: + * --option=arg + * --option arg + */ + string arg(argv[0]); + size_t arg_opt_start = arg.find_first_not_of('-'); + size_t arg_opt_sep = arg.find_first_of('='); + string option = arg.substr(arg_opt_start, arg_opt_sep - arg_opt_start); + + unsigned extra_argc_consumed = 0; + if (arg_opt_sep == string::npos) + { + /* no argument found => argument in argv[1] (maybe) */ + /* xxx, need to handle case where option isn't required */ +#if 0 + /* commented out, to return to true GNU style processing + * where longopts have to include an =, otherwise they are + * booleans */ + if (argc == 1) + { + return 0; /* run out of argv for argument */ + } + extra_argc_consumed = 1; +#endif + if(!storePair(opts, true, false, option, "1")) + { + return 0; + } + } + else + { + /* argument occurs after option_sep */ + string val = arg.substr(arg_opt_sep + 1); + storePair(opts, true, false, option, val); + } + + return extra_argc_consumed; + } + + unsigned parseSHORT(Options& opts, unsigned argc, const char* argv[]) + { + /* short options can take the forms: + * --option arg + * -option arg + */ + string arg(argv[0]); + size_t arg_opt_start = arg.find_first_not_of('-'); + string option = arg.substr(arg_opt_start); + /* lookup option */ + + /* argument in argv[1] */ + /* xxx, need to handle case where option isn't required */ + if (argc == 1) + { + cerr << "Not processing option without argument `" << option << "'" << endl; + return 0; /* run out of argv for argument */ + } + storePair(opts, false, true, option, string(argv[1])); + + return 1; + } + + list + scanArgv(Options& opts, unsigned argc, const char* argv[]) + { + /* a list for anything that didn't get handled as an option */ + list non_option_arguments; + + for(unsigned i = 1; i < argc; i++) + { + if (argv[i][0] != '-') + { + non_option_arguments.push_back(argv[i]); + continue; + } + + if (argv[i][1] == 0) + { + /* a lone single dash is an argument (usually signifying stdin) */ + non_option_arguments.push_back(argv[i]); + continue; + } + + if (argv[i][1] != '-') + { + /* handle short (single dash) options */ +#if 0 + i += parsePOSIX(opts, argc - i, &argv[i]); +#else + i += parseSHORT(opts, argc - i, &argv[i]); +#endif + continue; + } + + if (argv[i][2] == 0) + { + /* a lone double dash ends option processing */ + while (++i < argc) + { + non_option_arguments.push_back(argv[i]); + } + break; + } + + /* handle long (double dash) options */ + i += parseGNU(opts, argc - i, &argv[i]); + } + + return non_option_arguments; + } + + void scanLine(Options& opts, string& line) + { + /* strip any leading whitespace */ + size_t start = line.find_first_not_of(" \t\n\r"); + if (start == string::npos) + { + /* blank line */ + return; + } + if (line[start] == '#') + { + /* comment line */ + return; + } + /* look for first whitespace or ':' after the option end */ + size_t option_end = line.find_first_of(": \t\n\r",start); + string option = line.substr(start, option_end - start); + + /* look for ':', eat up any whitespace first */ + start = line.find_first_not_of(" \t\n\r", option_end); + if (start == string::npos) + { + /* error: badly formatted line */ + return; + } + if (line[start] != ':') + { + /* error: badly formatted line */ + return; + } + + /* look for start of value string -- eat up any leading whitespace */ + start = line.find_first_not_of(" \t\n\r", ++start); + if (start == string::npos) + { + /* error: badly formatted line */ + return; + } + + /* extract the value part, which may contain embedded spaces + * by searching for a word at a time, until we hit a comment or end of line */ + size_t value_end = start; + do + { + if (line[value_end] == '#') + { + /* rest of line is a comment */ + value_end--; + break; + } + value_end = line.find_first_of(" \t\n\r", value_end); + /* consume any white space, incase there is another word. + * any trailing whitespace will be removed shortly */ + value_end = line.find_first_not_of(" \t\n\r", value_end); + } while (value_end != string::npos); + /* strip any trailing space from value*/ + value_end = line.find_last_not_of(" \t\n\r", value_end); + + string value; + if (value_end >= start) + { + value = line.substr(start, value_end +1 - start); + } + else + { + /* error: no value */ + return; + } + + /* store the value in option */ + storePair(opts, true, false, option, value); + } + + void scanFile(Options& opts, istream& in) + { + do + { + string line; + getline(in, line); + scanLine(opts, line); + } while(!!in); + } + + /* for all options in opts, set their storage to their specified + * default value */ + void setDefaults(Options& opts) + { + for(Options::NamesPtrList::iterator it = opts.opt_list.begin(); it != opts.opt_list.end(); it++) + { + (*it)->opt->setDefault(); + } + } + + void parseConfigFile(Options& opts, const string& filename) + { + ifstream cfgstream(filename.c_str(), ifstream::in); + if (!cfgstream) + { + cerr << "Failed to open config file: `" << filename << "'" << endl; + exit(EXIT_FAILURE); + } + scanFile(opts, cfgstream); + } +} diff --git a/source/app/common/argument_parser/program_options_lite.h b/source/app/common/argument_parser/program_options_lite.h new file mode 100755 index 0000000..901c816 --- /dev/null +++ b/source/app/common/argument_parser/program_options_lite.h @@ -0,0 +1,231 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2015, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#include +#include +#include +#include +#include + +#ifndef __PROGRAM_OPTIONS_LITE__ +#define __PROGRAM_OPTIONS_LITE__ + +namespace program_options_lite +{ + struct Options; + + struct ParseFailure : public std::exception + { + ParseFailure(std::string arg0, std::string val0) throw() + : arg(arg0), val(val0) + {} + + ~ParseFailure() throw() {}; + + std::string arg; + std::string val; + + const char* what() const throw() { return "Option Parse Failure"; } + }; + + void doHelp(std::ostream& out, Options& opts, unsigned columns = 80); + unsigned parseGNU(Options& opts, unsigned argc, const char* argv[]); + unsigned parseSHORT(Options& opts, unsigned argc, const char* argv[]); + std::list scanArgv(Options& opts, unsigned argc, const char* argv[]); + void scanLine(Options& opts, std::string& line); + void scanFile(Options& opts, std::istream& in); + void setDefaults(Options& opts); + void parseConfigFile(Options& opts, const std::string& filename); + bool storePair(Options& opts, const std::string& name, const std::string& value); + + /** OptionBase: Virtual base class for storing information relating to a + * specific option This base class describes common elements. Type specific + * information should be stored in a derived class. */ + struct OptionBase + { + OptionBase(const std::string& name, const std::string& desc) + : opt_string(name), opt_desc(desc) + {}; + + virtual ~OptionBase() {} + + /* parse argument arg, to obtain a value for the option */ + virtual void parse(const std::string& arg) = 0; + /* set the argument to the default value */ + virtual void setDefault() = 0; + + std::string opt_string; + std::string opt_desc; + }; + + /** Type specific option storage */ + template + struct Option : public OptionBase + { + Option(const std::string& name, T& storage, T default_val, const std::string& desc) + : OptionBase(name, desc), opt_storage(storage), opt_default_val(default_val) + {} + + void parse(const std::string& arg); + + void setDefault() + { + opt_storage = opt_default_val; + } + + T& opt_storage; + T opt_default_val; + }; + + /* Generic parsing */ + template + inline void + Option::parse(const std::string& arg) + { + std::istringstream arg_ss (arg,std::istringstream::in); + arg_ss.exceptions(std::ios::failbit); + try + { + arg_ss >> opt_storage; + } + catch (...) + { + throw ParseFailure(opt_string, arg); + } + } + + /* string parsing is specialized -- copy the whole string, not just the + * first word */ + template<> + inline void + Option::parse(const std::string& arg) + { + opt_storage = arg; + } + + /** Option class for argument handling using a user provided function */ + struct OptionFunc : public OptionBase + { + typedef void (Func)(Options&, const std::string&); + + OptionFunc(const std::string& name, Options& parent_, Func *func_, const std::string& desc) + : OptionBase(name, desc), parent(parent_), func(func_) + {} + + void parse(const std::string& arg) + { + func(parent, arg); + } + + void setDefault() + { + return; + } + + private: + Options& parent; + void (*func)(Options&, const std::string&); + }; + + class OptionSpecific; + struct Options + { + ~Options(); + + OptionSpecific addOptions(); + + struct Names + { + Names() : opt(0) {}; + ~Names() + { + if (opt) + { + delete opt; + } + } + std::list opt_long; + std::list opt_short; + OptionBase* opt; + }; + + void addOption(OptionBase *opt); + + typedef std::list NamesPtrList; + NamesPtrList opt_list; + + typedef std::map NamesMap; + NamesMap opt_long_map; + NamesMap opt_short_map; + }; + + /* Class with templated overloaded operator(), for use by Options::addOptions() */ + class OptionSpecific + { + public: + OptionSpecific(Options& parent_) : parent(parent_) {} + + /** + * Add option described by name to the parent Options list, + * with storage for the option's value + * with default_val as the default value + * with desc as an optional help description + */ + template + OptionSpecific& + operator()(const std::string& name, T& storage, T default_val, const std::string& desc = "") + { + parent.addOption(new Option(name, storage, default_val, desc)); + return *this; + } + + /** + * Add option described by name to the parent Options list, + * with desc as an optional help description + * instead of storing the value somewhere, a function of type + * OptionFunc::Func is called. It is upto this function to correctly + * handle evaluating the option's value. + */ + OptionSpecific& + operator()(const std::string& name, OptionFunc::Func *func, const std::string& desc = "") + { + parent.addOption(new OptionFunc(name, parent, func, desc)); + return *this; + } + private: + Options& parent; + }; + +} /* namespace: program_options_lite */ + + +#endif // __PROGRAM_OPTIONS_LITE__ diff --git a/source/app/common/cJSON/CMakeLists.txt b/source/app/common/cJSON/CMakeLists.txt new file mode 100644 index 0000000..0c5348e --- /dev/null +++ b/source/app/common/cJSON/CMakeLists.txt @@ -0,0 +1,16 @@ +# library +set( LIB_NAME cJSON ) + +# get source files +file( GLOB SRC_FILES "*.c" "*.cpp" ) + +# get include files +file( GLOB INC_FILES "*.h" ) + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/source/app/common/cJSON/cJSON.c b/source/app/common/cJSON/cJSON.c new file mode 100755 index 0000000..aa6e563 --- /dev/null +++ b/source/app/common/cJSON/cJSON.c @@ -0,0 +1,2656 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +/* cJSON */ +/* JSON parser in C. */ + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + +#include "cJSON.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; + +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) +{ + return (const char*) (global_error.json + global_error.position); +} + +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 5) || (CJSON_VERSION_PATCH != 5) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) +{ + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(*allocate)(size_t size); + void (*deallocate)(void *pointer); + void *(*reallocate)(void *pointer, size_t size); +} internal_hooks; + +static internal_hooks global_hooks = { malloc, free, realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + if (!(copy = (unsigned char*)hooks->allocate(length))) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; + return; + } + + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } +} + +/* Internal constructor. */ +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) +{ + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; +} + +/* Delete a cJSON structure. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) +{ + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } +} + +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) +{ + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +} + +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; + +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +#define cannot_read(buffer, size) (!can_read(buffer, size)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) + +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); +} + +/* Render the number nicely from the given item into a string. */ +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occured */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; +} + +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) +{ + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; +} + +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) +{ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; + + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); + + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } + + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ + + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } + + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } + + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } + + + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; +} + +/* Render the cstring provided to an escaped version that can be printed. */ +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) +{ + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; + + if (output_buffer == NULL) + { + return false; + } + + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); + + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); +} + +/* Predeclare these prototypes. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); + +/* Utility to jump whitespace and cr/lf */ +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* Parse an object - create a new root, and populate. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) +{ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; + + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; + + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(&buffer))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + else + { + global_error = local_error; + } + } + + return NULL; +} + +/* Default options for cJSON_Parse */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(256); + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->length); + buffer->buffer = NULL; + if (printed == NULL) { + goto fail; + } + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} + +/* Render a cJSON item/entity/structure to text. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (len < 0) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} + +/* Parser core - when encountering text, process appropriately. */ +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) +{ + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } + + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + + return false; +} + +/* Render a value to text. */ +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + if (!output_buffer->noalloc) + { + output_buffer->hooks.deallocate(output_buffer->buffer); + } + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } +} + +/* Build an array from input text. */ +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an array to text */ +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer = '['; + output_buffer->offset++; + output_buffer->depth++; + + while (current_element != NULL) + { + if (!print_value(current_element, output_buffer)) + { + return false; + } + update_offset(output_buffer); + if (current_element->next) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Build an object from the text. */ +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) +{ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; + + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } + + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } + + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* faile to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; +} + +/* Render an object to text. */ +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) +{ + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_item = item->child; + + if (output_buffer == NULL) + { + return false; + } + + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + + *output_pointer++ = '{'; + output_buffer->depth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; + + while (current_item) + { + if (output_buffer->format) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } + + /* print key */ + if (!print_string_ptr((unsigned char*)current_item->string, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; +} + +/* Get Array size/item / object item. */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *c = array->child; + size_t i = 0; + while(c) + { + i++; + c = c->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)i; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} + +/* Utility for array list handling. */ +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + +/* Utility for handling references. */ +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *ref = cJSON_New_Item(hooks); + if (!ref) + { + return NULL; + } + memcpy(ref, item, sizeof(cJSON)); + ref->string = NULL; + ref->type |= cJSON_IsReference; + ref->next = ref->prev = NULL; + return ref; +} + +/* Add item to array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) + { + return; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + /* call cJSON_AddItemToObjectCS for code reuse */ + cJSON_AddItemToObjectCS(object, (char*)cJSON_strdup((const unsigned char*)string, &global_hooks), item); + /* remove cJSON_StringIsConst flag */ + item->type &= ~cJSON_StringIsConst; +} + +#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + if (!item) + { + return; + } + if (!(item->type & cJSON_StringIsConst) && item->string) + { + global_hooks.deallocate(item->string); + } + item->string = (char*)string; + item->type |= cJSON_StringIsConst; + cJSON_AddItemToArray(object, item); +} +#if defined (__clang__) || ((__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + cJSON_AddItemToArray(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + cJSON_AddItemToObject(object, string, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); +} + +/* Replace array/object items with new ones. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + cJSON_AddItemToArray(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if (replacement == NULL) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} + +/* Create basic types: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} + +/* Create Arrays: */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if (count < 0) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if (count < 0) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if (count < 0) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if (count < 0) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} + +/* Duplication */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) +{ + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; +} + +CJSON_PUBLIC(void) cJSON_Minify(char *json) +{ + unsigned char *into = (unsigned char*)json; + while (*json) + { + if (*json == ' ') + { + json++; + } + else if (*json == '\t') + { + /* Whitespace characters. */ + json++; + } + else if (*json == '\r') + { + json++; + } + else if (*json=='\n') + { + json++; + } + else if ((*json == '/') && (json[1] == '/')) + { + /* double-slash comments, to end of line. */ + while (*json && (*json != '\n')) + { + json++; + } + } + else if ((*json == '/') && (json[1] == '*')) + { + /* multiline comments. */ + while (*json && !((*json == '*') && (json[1] == '/'))) + { + json++; + } + json += 2; + } + else if (*json == '\"') + { + /* string literals, which are \" sensitive. */ + *into++ = (unsigned char)*json++; + while (*json && (*json != '\"')) + { + if (*json == '\\') + { + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + else + { + /* All other characters. */ + *into++ = (unsigned char)*json++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); +} diff --git a/source/app/common/cJSON/cJSON.h b/source/app/common/cJSON/cJSON.h new file mode 100755 index 0000000..2af0a9c --- /dev/null +++ b/source/app/common/cJSON/cJSON.h @@ -0,0 +1,263 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef cJSON__h +#define cJSON__h + +#ifdef __cplusplus +extern "C" +{ +#endif + +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 5 +#define CJSON_VERSION_PATCH 5 + +#include + +/* cJSON Types: */ +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ + +#define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 + +/* The cJSON structure: */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; + + /* The type of the item, as above. */ + int type; + + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; + + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; +} cJSON; + +typedef struct cJSON_Hooks +{ + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); +} cJSON_Hooks; + +typedef int cJSON_bool; + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type __stdcall +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall +#endif +#else /* !WIN32 */ +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + +/* Supply malloc, realloc and free functions to cJSON */ +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); + +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); +/* Delete a cJSON entity and all subentities. */ +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); + +/* Returns the number of items in an array (or object). */ +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); +/* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); +/* Get item "string" from object. Case insensitive. */ +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); +/* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); + +/* These calls create a cJSON item of the appropriate type. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* These utilities create an Array of count items. */ +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); + +/* Append item to the specified array/object. */ +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); +/* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); + +/* Remove/Detatch items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); + +/* Update array items. */ +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); + +/* Duplicate a cJSON item */ +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); +/* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will +need to be released. With recurse!=0, it will duplicate any children connected to the item. +The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); + + +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error. If not, then cJSON_GetErrorPtr() does the job. */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); + +CJSON_PUBLIC(void) cJSON_Minify(char *json); + +/* Macros for creating things quickly. */ +#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) +#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) +#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) +#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) +#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) +#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) +#define cJSON_AddRawToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateRaw(s)) + +/* When assigning an integer value, it needs to be propagated to valuedouble too. */ +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/app/common/cJSON/cJSON_Utils.c b/source/app/common/cJSON/cJSON_Utils.c new file mode 100755 index 0000000..c0fd649 --- /dev/null +++ b/source/app/common/cJSON/cJSON_Utils.c @@ -0,0 +1,1388 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#pragma GCC visibility push(default) +#include +#include +#include +#include +#include +#pragma GCC visibility pop + +#include "cJSON_Utils.h" + +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) + +static unsigned char* cJSONUtils_strdup(const unsigned char* const string) +{ + size_t length = 0; + unsigned char *copy = NULL; + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*) cJSON_malloc(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +/* string comparison which doesn't consider NULL pointers equal */ +static int compare_strings(const unsigned char *string1, const unsigned char *string2, const cJSON_bool case_sensitive) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + if (case_sensitive) + { + return strcmp((const char*)string1, (const char*)string2); + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +/* Compare the next path element of two JSON pointers, two NULL pointers are considered unequal: */ +static cJSON_bool compare_pointers(const unsigned char *name, const unsigned char *pointer, const cJSON_bool case_sensitive) +{ + if ((name == NULL) || (pointer == NULL)) + { + return false; + } + + for (; (*name != '\0') && (*pointer != '\0') && (*pointer != '/'); (void)name++, pointer++) /* compare until next '/' */ + { + if (*pointer == '~') + { + /* check for escaped '~' (~0) and '/' (~1) */ + if (((pointer[1] != '0') || (*name != '~')) && ((pointer[1] != '1') || (*name != '/'))) + { + /* invalid escape sequence or wrong character in *name */ + return false; + } + else + { + pointer++; + } + } + else if ((!case_sensitive && (tolower(*name) != tolower(*pointer))) || (case_sensitive && (*name != *pointer))) + { + return false; + } + } + if (((*pointer != 0) && (*pointer != '/')) != (*name != 0)) + { + /* one string has ended, the other not */ + return false;; + } + + return true; +} + +/* calculate the length of a string if encoded as JSON pointer with ~0 and ~1 escape sequences */ +static size_t pointer_encoded_length(const unsigned char *string) +{ + size_t length; + for (length = 0; *string != '\0'; (void)string++, length++) + { + /* character needs to be escaped? */ + if ((*string == '~') || (*string == '/')) + { + length++; + } + } + + return length; +} + +/* copy a string while escaping '~' and '/' with ~0 and ~1 JSON pointer escape codes */ +static void encode_string_as_pointer(unsigned char *destination, const unsigned char *source) +{ + for (; source[0] != '\0'; (void)source++, destination++) + { + if (source[0] == '/') + { + destination[1] = '1'; + destination++; + } + else if (source[0] == '~') + { + destination[0] = '~'; + destination[1] = '1'; + destination++; + } + else + { + destination[0] = source[0]; + } + } + + destination[0] = '\0'; +} + +CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target) +{ + size_t child_index = 0; + cJSON *current_child = 0; + + if (object == target) + { + /* found */ + return (char*)cJSONUtils_strdup((const unsigned char*)""); + } + + /* recursively search all children of the object or array */ + for (current_child = object->child; current_child != NULL; (void)(current_child = current_child->next), child_index++) + { + unsigned char *target_pointer = (unsigned char*)cJSONUtils_FindPointerFromObjectTo(current_child, target); + /* found the target? */ + if (target_pointer != NULL) + { + if (cJSON_IsArray(object)) + { + /* reserve enough memory for a 64 bit integer + '/' and '\0' */ + unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + 20 + sizeof("/")); + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (child_index > ULONG_MAX) + { + cJSON_free(target_pointer); + return NULL; + } + sprintf((char*)full_pointer, "/%lu%s", (unsigned long)child_index, target_pointer); /* / */ + cJSON_free(target_pointer); + + return (char*)full_pointer; + } + + if (cJSON_IsObject(object)) + { + unsigned char *full_pointer = (unsigned char*)cJSON_malloc(strlen((char*)target_pointer) + pointer_encoded_length((unsigned char*)current_child->string) + 2); + full_pointer[0] = '/'; + encode_string_as_pointer(full_pointer + 1, (unsigned char*)current_child->string); + strcat((char*)full_pointer, (char*)target_pointer); + cJSON_free(target_pointer); + + return (char*)full_pointer; + } + + /* reached leaf of the tree, found nothing */ + cJSON_free(target_pointer); + return NULL; + } + } + + /* not found */ + return NULL; +} + +/* non broken version of cJSON_GetArrayItem */ +static cJSON *get_array_item(const cJSON *array, size_t item) +{ + cJSON *child = array ? array->child : NULL; + while ((child != NULL) && (item > 0)) + { + item--; + child = child->next; + } + + return child; +} + +static cJSON_bool decode_array_index_from_pointer(const unsigned char * const pointer, size_t * const index) +{ + size_t parsed_index = 0; + size_t position = 0; + + if ((pointer[0] == '0') && ((pointer[1] != '\0') && (pointer[1] != '/'))) + { + /* leading zeroes are not permitted */ + return 0; + } + + for (position = 0; (pointer[position] >= '0') && (pointer[0] <= '9'); position++) + { + parsed_index = (10 * parsed_index) + (size_t)(pointer[position] - '0'); + + } + + if ((pointer[position] != '\0') && (pointer[position] != '/')) + { + return 0; + } + + *index = parsed_index; + + return 1; +} + +static cJSON *get_item_from_pointer(cJSON * const object, const char * pointer, const cJSON_bool case_sensitive) +{ + cJSON *current_element = object; + /* follow path of the pointer */ + while ((pointer[0] == '/') && (current_element != NULL)) + { + pointer++; + if (cJSON_IsArray(current_element)) + { + size_t index = 0; + if (!decode_array_index_from_pointer((const unsigned char*)pointer, &index)) + { + return NULL; + } + + current_element = get_array_item(current_element, index); + } + else if (cJSON_IsObject(current_element)) + { + current_element = current_element->child; + /* GetObjectItem. */ + while ((current_element != NULL) && !compare_pointers((unsigned char*)current_element->string, (const unsigned char*)pointer, case_sensitive)) + { + current_element = current_element->next; + } + } + else + { + return NULL; + } + + /* skip to the next path token or end of string */ + while ((pointer[0] != '\0') && (pointer[0] != '/')) + { + pointer++; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer) +{ + return get_item_from_pointer(object, pointer, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer) +{ + return get_item_from_pointer(object, pointer, true); +} + +/* JSON Patch implementation. */ +static void decode_pointer_inplace(unsigned char *string) +{ + unsigned char *decoded_string = string; + + if (string == NULL) { + return; + } + + for (; *string; (void)decoded_string++, string++) + { + if (string[0] == '~') + { + if (string[1] == '0') + { + decoded_string[0] = '~'; + } + else if (string[1] == '1') + { + decoded_string[1] = '/'; + } + else + { + /* invalid escape sequence */ + return; + } + + string++; + } + } + + decoded_string[0] = '\0'; +} + +/* non-broken cJSON_DetachItemFromArray */ +static cJSON *detach_item_from_array(cJSON *array, size_t which) +{ + cJSON *c = array->child; + while (c && (which > 0)) + { + c = c->next; + which--; + } + if (!c) + { + /* item doesn't exist */ + return NULL; + } + if (c->prev) + { + /* not the first element */ + c->prev->next = c->next; + } + if (c->next) + { + c->next->prev = c->prev; + } + if (c==array->child) + { + array->child = c->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + c->prev = c->next = NULL; + + return c; +} + +/* detach an item at the given path */ +static cJSON *detach_path(cJSON *object, const unsigned char *path, const cJSON_bool case_sensitive) +{ + unsigned char *parent_pointer = NULL; + unsigned char *child_pointer = NULL; + cJSON *parent = NULL; + cJSON *detached_item = NULL; + + /* copy path and split it in parent and child */ + parent_pointer = cJSONUtils_strdup(path); + if (parent_pointer == NULL) { + goto cleanup; + } + + child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); /* last '/' */ + if (child_pointer == NULL) + { + goto cleanup; + } + /* split strings */ + child_pointer[0] = '\0'; + child_pointer++; + + parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); + decode_pointer_inplace(child_pointer); + + if (cJSON_IsArray(parent)) + { + size_t index = 0; + if (!decode_array_index_from_pointer(child_pointer, &index)) + { + goto cleanup; + } + detached_item = detach_item_from_array(parent, index); + } + else if (cJSON_IsObject(parent)) + { + detached_item = cJSON_DetachItemFromObject(parent, (char*)child_pointer); + } + else + { + /* Couldn't find object to remove child from. */ + goto cleanup; + } + +cleanup: + if (parent_pointer != NULL) + { + cJSON_free(parent_pointer); + } + + return detached_item; +} + +/* sort lists using mergesort */ +static cJSON *sort_list(cJSON *list, const cJSON_bool case_sensitive) +{ + cJSON *first = list; + cJSON *second = list; + cJSON *current_item = list; + cJSON *result = list; + cJSON *result_tail = NULL; + + if ((list == NULL) || (list->next == NULL)) + { + /* One entry is sorted already. */ + return result; + } + + while ((current_item != NULL) && (current_item->next != NULL) && (compare_strings((unsigned char*)current_item->string, (unsigned char*)current_item->next->string, case_sensitive) < 0)) + { + /* Test for list sorted. */ + current_item = current_item->next; + } + if ((current_item == NULL) || (current_item->next == NULL)) + { + /* Leave sorted lists unmodified. */ + return result; + } + + /* reset pointer to the beginning */ + current_item = list; + while (current_item != NULL) + { + /* Walk two pointers to find the middle. */ + second = second->next; + current_item = current_item->next; + /* advances current_item two steps at a time */ + if (current_item != NULL) + { + current_item = current_item->next; + } + } + if ((second != NULL) && (second->prev != NULL)) + { + /* Split the lists */ + second->prev->next = NULL; + } + + /* Recursively sort the sub-lists. */ + first = sort_list(first, case_sensitive); + second = sort_list(second, case_sensitive); + result = NULL; + + /* Merge the sub-lists */ + while ((first != NULL) && (second != NULL)) + { + cJSON *smaller = NULL; + if (compare_strings((unsigned char*)first->string, (unsigned char*)second->string, false) < 0) + { + smaller = first; + } + else + { + smaller = second; + } + + if (result == NULL) + { + /* start merged list with the smaller element */ + result_tail = smaller; + result = smaller; + } + else + { + /* add smaller element to the list */ + result_tail->next = smaller; + smaller->prev = result_tail; + result_tail = smaller; + } + + if (first == smaller) + { + first = first->next; + } + else + { + second = second->next; + } + } + + if (first != NULL) + { + /* Append rest of first list. */ + if (result == NULL) + { + return first; + } + result_tail->next = first; + first->prev = result_tail; + } + if (second != NULL) + { + /* Append rest of second list */ + if (result == NULL) + { + return second; + } + result_tail->next = second; + second->prev = result_tail; + } + + return result; +} + +static void sort_object(cJSON * const object, const cJSON_bool case_sensitive) +{ + object->child = sort_list(object->child, case_sensitive); +} + +static cJSON_bool compare_json(cJSON *a, cJSON *b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF))) + { + /* mismatched type. */ + return false; + } + switch (a->type & 0xFF) + { + case cJSON_Number: + /* numeric mismatch. */ + if ((a->valueint != b->valueint) || (a->valuedouble != b->valuedouble)) + { + return false; + } + else + { + return true; + } + + case cJSON_String: + /* string mismatch. */ + if (strcmp(a->valuestring, b->valuestring) != 0) + { + return false; + } + else + { + return true; + } + + case cJSON_Array: + for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next) + { + cJSON_bool identical = compare_json(a, b, case_sensitive); + if (!identical) + { + return false; + } + } + + /* array size mismatch? (one of both children is not NULL) */ + if ((a != NULL) || (b != NULL)) + { + return false; + } + else + { + return true; + } + + case cJSON_Object: + sort_object(a, case_sensitive); + sort_object(b, case_sensitive); + for ((void)(a = a->child), b = b->child; (a != NULL) && (b != NULL); (void)(a = a->next), b = b->next) + { + cJSON_bool identical = false; + /* compare object keys */ + if (compare_strings((unsigned char*)a->string, (unsigned char*)b->string, case_sensitive)) + { + /* missing member */ + return false; + } + identical = compare_json(a, b, case_sensitive); + if (!identical) + { + return false; + } + } + + /* object length mismatch (one of both children is not null) */ + if ((a != NULL) || (b != NULL)) + { + return false; + } + else + { + return true; + } + + default: + break; + } + + /* null, true or false */ + return true; +} + +/* non broken version of cJSON_InsertItemInArray */ +static cJSON_bool insert_item_in_array(cJSON *array, size_t which, cJSON *newitem) +{ + cJSON *child = array->child; + while (child && (which > 0)) + { + child = child->next; + which--; + } + if (which > 0) + { + /* item is after the end of the array */ + return 0; + } + if (child == NULL) + { + cJSON_AddItemToArray(array, newitem); + return 1; + } + + /* insert into the linked list */ + newitem->next = child; + newitem->prev = child->prev; + child->prev = newitem; + + /* was it at the beginning */ + if (child == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } + + return 1; +} + +static cJSON *get_object_item(const cJSON * const object, const char* name, const cJSON_bool case_sensitive) +{ + if (case_sensitive) + { + return cJSON_GetObjectItemCaseSensitive(object, name); + } + + return cJSON_GetObjectItem(object, name); +} + +enum patch_operation { INVALID, ADD, REMOVE, REPLACE, MOVE, COPY, TEST }; + +static enum patch_operation decode_patch_operation(const cJSON * const patch, const cJSON_bool case_sensitive) +{ + cJSON *operation = get_object_item(patch, "op", case_sensitive); + if (!cJSON_IsString(operation)) + { + return INVALID; + } + + if (strcmp(operation->valuestring, "add") == 0) + { + return ADD; + } + + if (strcmp(operation->valuestring, "remove") == 0) + { + return REMOVE; + } + + if (strcmp(operation->valuestring, "replace") == 0) + { + return REPLACE; + } + + if (strcmp(operation->valuestring, "move") == 0) + { + return MOVE; + } + + if (strcmp(operation->valuestring, "copy") == 0) + { + return COPY; + } + + if (strcmp(operation->valuestring, "test") == 0) + { + return TEST; + } + + return INVALID; +} + +/* overwrite and existing item with another one and free resources on the way */ +static void overwrite_item(cJSON * const root, const cJSON replacement) +{ + if (root == NULL) + { + return; + } + + if (root->string != NULL) + { + cJSON_free(root->string); + } + if (root->valuestring != NULL) + { + cJSON_free(root->valuestring); + } + if (root->child != NULL) + { + cJSON_Delete(root->child); + } + + memcpy(root, &replacement, sizeof(cJSON)); +} + +static int apply_patch(cJSON *object, const cJSON *patch, const cJSON_bool case_sensitive) +{ + cJSON *path = NULL; + cJSON *value = NULL; + cJSON *parent = NULL; + enum patch_operation opcode = INVALID; + unsigned char *parent_pointer = NULL; + unsigned char *child_pointer = NULL; + int status = 0; + + path = get_object_item(patch, "path", case_sensitive); + if (!cJSON_IsString(path)) + { + /* malformed patch. */ + status = 2; + goto cleanup; + } + + opcode = decode_patch_operation(patch, case_sensitive); + if (opcode == INVALID) + { + status = 3; + goto cleanup; + } + else if (opcode == TEST) + { + /* compare value: {...} with the given path */ + status = !compare_json(get_item_from_pointer(object, path->valuestring, case_sensitive), get_object_item(patch, "value", case_sensitive), case_sensitive); + goto cleanup; + } + + /* special case for replacing the root */ + if (path->valuestring[0] == '\0') + { + if (opcode == REMOVE) + { + static const cJSON invalid = { NULL, NULL, NULL, cJSON_Invalid, NULL, 0, 0, NULL}; + + overwrite_item(object, invalid); + + status = 0; + goto cleanup; + } + + if ((opcode == REPLACE) || (opcode == ADD)) + { + value = get_object_item(patch, "value", case_sensitive); + if (value == NULL) + { + /* missing "value" for add/replace. */ + status = 7; + goto cleanup; + } + + value = cJSON_Duplicate(value, 1); + if (value == NULL) + { + /* out of memory for add/replace. */ + status = 8; + goto cleanup; + } + + overwrite_item(object, *value); + + /* delete the duplicated value */ + cJSON_free(value); + value = NULL; + + /* the string "value" isn't needed */ + if (object->string != NULL) + { + cJSON_free(object->string); + object->string = NULL; + } + + status = 0; + goto cleanup; + } + } + + if ((opcode == REMOVE) || (opcode == REPLACE)) + { + /* Get rid of old. */ + cJSON *old_item = detach_path(object, (unsigned char*)path->valuestring, case_sensitive); + if (old_item == NULL) + { + status = 13; + goto cleanup; + } + cJSON_Delete(old_item); + if (opcode == REMOVE) + { + /* For Remove, this job is done. */ + status = 0; + goto cleanup; + } + } + + /* Copy/Move uses "from". */ + if ((opcode == MOVE) || (opcode == COPY)) + { + cJSON *from = get_object_item(patch, "from", case_sensitive); + if (from == NULL) + { + /* missing "from" for copy/move. */ + status = 4; + goto cleanup; + } + + if (opcode == MOVE) + { + value = detach_path(object, (unsigned char*)from->valuestring, case_sensitive); + } + if (opcode == COPY) + { + value = get_item_from_pointer(object, from->valuestring, case_sensitive); + } + if (value == NULL) + { + /* missing "from" for copy/move. */ + status = 5; + goto cleanup; + } + if (opcode == COPY) + { + value = cJSON_Duplicate(value, 1); + } + if (value == NULL) + { + /* out of memory for copy/move. */ + status = 6; + goto cleanup; + } + } + else /* Add/Replace uses "value". */ + { + value = get_object_item(patch, "value", case_sensitive); + if (value == NULL) + { + /* missing "value" for add/replace. */ + status = 7; + goto cleanup; + } + value = cJSON_Duplicate(value, 1); + if (value == NULL) + { + /* out of memory for add/replace. */ + status = 8; + goto cleanup; + } + } + + /* Now, just add "value" to "path". */ + + /* split pointer in parent and child */ + parent_pointer = cJSONUtils_strdup((unsigned char*)path->valuestring); + child_pointer = (unsigned char*)strrchr((char*)parent_pointer, '/'); + if (child_pointer != NULL) + { + child_pointer[0] = '\0'; + child_pointer++; + } + parent = get_item_from_pointer(object, (char*)parent_pointer, case_sensitive); + decode_pointer_inplace(child_pointer); + + /* add, remove, replace, move, copy, test. */ + if ((parent == NULL) || (child_pointer == NULL)) + { + /* Couldn't find object to add to. */ + status = 9; + goto cleanup; + } + else if (cJSON_IsArray(parent)) + { + if (strcmp((char*)child_pointer, "-") == 0) + { + cJSON_AddItemToArray(parent, value); + value = NULL; + } + else + { + size_t index = 0; + if (!decode_array_index_from_pointer(child_pointer, &index)) + { + status = 11; + goto cleanup; + } + + if (!insert_item_in_array(parent, index, value)) + { + status = 10; + goto cleanup; + } + value = NULL; + } + } + else if (cJSON_IsObject(parent)) + { + if (case_sensitive) + { + cJSON_DeleteItemFromObjectCaseSensitive(parent, (char*)child_pointer); + } + else + { + cJSON_DeleteItemFromObject(parent, (char*)child_pointer); + } + cJSON_AddItemToObject(parent, (char*)child_pointer, value); + value = NULL; + } + +cleanup: + if (value != NULL) + { + cJSON_Delete(value); + } + if (parent_pointer != NULL) + { + cJSON_free(parent_pointer); + } + + return status; +} + +CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches) +{ + const cJSON *current_patch = NULL; + int status = 0; + + if (!cJSON_IsArray(patches)) + { + /* malformed patches. */ + return 1; + } + + if (patches != NULL) + { + current_patch = patches->child; + } + + while (current_patch != NULL) + { + status = apply_patch(object, current_patch, false); + if (status != 0) + { + return status; + } + current_patch = current_patch->next; + } + + return 0; +} + +CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches) +{ + const cJSON *current_patch = NULL; + int status = 0; + + if (!cJSON_IsArray(patches)) + { + /* malformed patches. */ + return 1; + } + + if (patches != NULL) + { + current_patch = patches->child; + } + + while (current_patch != NULL) + { + status = apply_patch(object, current_patch, true); + if (status != 0) + { + return status; + } + current_patch = current_patch->next; + } + + return 0; +} + +static void compose_patch(cJSON * const patches, const unsigned char * const operation, const unsigned char * const path, const unsigned char *suffix, const cJSON * const value) +{ + cJSON *patch = cJSON_CreateObject(); + if (patch == NULL) + { + return; + } + cJSON_AddItemToObject(patch, "op", cJSON_CreateString((const char*)operation)); + + if (suffix == NULL) + { + cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)path)); + } + else + { + size_t suffix_length = pointer_encoded_length(suffix); + size_t path_length = strlen((const char*)path); + unsigned char *full_path = (unsigned char*)cJSON_malloc(path_length + suffix_length + sizeof("/")); + + sprintf((char*)full_path, "%s/", (const char*)path); + encode_string_as_pointer(full_path + path_length + 1, suffix); + + cJSON_AddItemToObject(patch, "path", cJSON_CreateString((const char*)full_path)); + cJSON_free(full_path); + } + + if (value != NULL) + { + cJSON_AddItemToObject(patch, "value", cJSON_Duplicate(value, 1)); + } + cJSON_AddItemToArray(patches, patch); +} + +CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value) +{ + compose_patch(array, (const unsigned char*)operation, (const unsigned char*)path, NULL, value); +} + +static void create_patches(cJSON * const patches, const unsigned char * const path, cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive) +{ + if ((from == NULL) || (to == NULL)) + { + return; + } + + if ((from->type & 0xFF) != (to->type & 0xFF)) + { + compose_patch(patches, (const unsigned char*)"replace", path, 0, to); + return; + } + + switch (from->type & 0xFF) + { + case cJSON_Number: + if ((from->valueint != to->valueint) || (from->valuedouble != to->valuedouble)) + { + compose_patch(patches, (const unsigned char*)"replace", path, NULL, to); + } + return; + + case cJSON_String: + if (strcmp(from->valuestring, to->valuestring) != 0) + { + compose_patch(patches, (const unsigned char*)"replace", path, NULL, to); + } + return; + + case cJSON_Array: + { + size_t index = 0; + cJSON *from_child = from->child; + cJSON *to_child = to->child; + unsigned char *new_path = (unsigned char*)cJSON_malloc(strlen((const char*)path) + 20 + sizeof("/")); /* Allow space for 64bit int. log10(2^64) = 20 */ + + /* generate patches for all array elements that exist in both "from" and "to" */ + for (index = 0; (from_child != NULL) && (to_child != NULL); (void)(from_child = from_child->next), (void)(to_child = to_child->next), index++) + { + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (index > ULONG_MAX) + { + cJSON_free(new_path); + return; + } + sprintf((char*)new_path, "%s/%lu", path, (unsigned long)index); /* path of the current array element */ + create_patches(patches, new_path, from_child, to_child, case_sensitive); + } + + /* remove leftover elements from 'from' that are not in 'to' */ + for (; (from_child != NULL); (void)(from_child = from_child->next)) + { + /* check if conversion to unsigned long is valid + * This should be eliminated at compile time by dead code elimination + * if size_t is an alias of unsigned long, or if it is bigger */ + if (index > ULONG_MAX) + { + cJSON_free(new_path); + return; + } + sprintf((char*)new_path, "%lu", (unsigned long)index); + compose_patch(patches, (const unsigned char*)"remove", path, new_path, NULL); + } + /* add new elements in 'to' that were not in 'from' */ + for (; (to_child != NULL); (void)(to_child = to_child->next), index++) + { + compose_patch(patches, (const unsigned char*)"add", path, (const unsigned char*)"-", to_child); + } + cJSON_free(new_path); + return; + } + + case cJSON_Object: + { + cJSON *from_child = NULL; + cJSON *to_child = NULL; + sort_object(from, case_sensitive); + sort_object(to, case_sensitive); + + from_child = from->child; + to_child = to->child; + /* for all object values in the object with more of them */ + while ((from_child != NULL) || (to_child != NULL)) + { + int diff; + if (from_child == NULL) + { + diff = 1; + } + else if (to_child == NULL) + { + diff = -1; + } + else + { + diff = compare_strings((unsigned char*)from_child->string, (unsigned char*)to_child->string, case_sensitive); + } + + if (diff == 0) + { + /* both object keys are the same */ + size_t path_length = strlen((const char*)path); + size_t from_child_name_length = pointer_encoded_length((unsigned char*)from_child->string); + unsigned char *new_path = (unsigned char*)cJSON_malloc(path_length + from_child_name_length + sizeof("/")); + + sprintf((char*)new_path, "%s/", path); + encode_string_as_pointer(new_path + path_length + 1, (unsigned char*)from_child->string); + + /* create a patch for the element */ + create_patches(patches, new_path, from_child, to_child, case_sensitive); + cJSON_free(new_path); + + from_child = from_child->next; + to_child = to_child->next; + } + else if (diff < 0) + { + /* object element doesn't exist in 'to' --> remove it */ + compose_patch(patches, (const unsigned char*)"remove", path, (unsigned char*)from_child->string, NULL); + + from_child = from_child->next; + } + else + { + /* object element doesn't exist in 'from' --> add it */ + compose_patch(patches, (const unsigned char*)"add", path, (unsigned char*)to_child->string, to_child); + + to_child = to_child->next; + } + } + return; + } + + default: + break; + } +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to) +{ + cJSON *patches = cJSON_CreateArray(); + create_patches(patches, (const unsigned char*)"", from, to, false); + + return patches; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to) +{ + cJSON *patches = cJSON_CreateArray(); + create_patches(patches, (const unsigned char*)"", from, to, true); + + return patches; +} + +CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object) +{ + sort_object(object, false); +} + +CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object) +{ + sort_object(object, true); +} + +static cJSON *merge_patch(cJSON *target, const cJSON * const patch, const cJSON_bool case_sensitive) +{ + cJSON *patch_child = NULL; + + if (!cJSON_IsObject(patch)) + { + /* scalar value, array or NULL, just duplicate */ + cJSON_Delete(target); + return cJSON_Duplicate(patch, 1); + } + + if (!cJSON_IsObject(target)) + { + cJSON_Delete(target); + target = cJSON_CreateObject(); + } + + patch_child = patch->child; + while (patch_child != NULL) + { + if (cJSON_IsNull(patch_child)) + { + /* NULL is the indicator to remove a value, see RFC7396 */ + if (case_sensitive) + { + cJSON_DeleteItemFromObjectCaseSensitive(target, patch_child->string); + } + else + { + cJSON_DeleteItemFromObject(target, patch_child->string); + } + } + else + { + cJSON *replace_me = NULL; + cJSON *replacement = NULL; + + if (case_sensitive) + { + replace_me = cJSON_DetachItemFromObjectCaseSensitive(target, patch_child->string); + } + else + { + replace_me = cJSON_DetachItemFromObject(target, patch_child->string); + } + + replacement = merge_patch(replace_me, patch_child, case_sensitive); + if (replacement == NULL) + { + return NULL; + } + + cJSON_AddItemToObject(target, patch_child->string, replacement); + } + patch_child = patch_child->next; + } + return target; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch) +{ + return merge_patch(target, patch, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch) +{ + return merge_patch(target, patch, true); +} + +static cJSON *generate_merge_patch(cJSON * const from, cJSON * const to, const cJSON_bool case_sensitive) +{ + cJSON *from_child = NULL; + cJSON *to_child = NULL; + cJSON *patch = NULL; + if (to == NULL) + { + /* patch to delete everything */ + return cJSON_CreateNull(); + } + if (!cJSON_IsObject(to) || !cJSON_IsObject(from)) + { + return cJSON_Duplicate(to, 1); + } + + sort_object(from, case_sensitive); + sort_object(to, case_sensitive); + + from_child = from->child; + to_child = to->child; + patch = cJSON_CreateObject(); + while (from_child || to_child) + { + int diff; + if (from_child != NULL) + { + if (to_child != NULL) + { + diff = strcmp(from_child->string, to_child->string); + } + else + { + diff = -1; + } + } + else + { + diff = 1; + } + + if (diff < 0) + { + /* from has a value that to doesn't have -> remove */ + cJSON_AddItemToObject(patch, from_child->string, cJSON_CreateNull()); + + from_child = from_child->next; + } + else if (diff > 0) + { + /* to has a value that from doesn't have -> add to patch */ + cJSON_AddItemToObject(patch, to_child->string, cJSON_Duplicate(to_child, 1)); + + to_child = to_child->next; + } + else + { + /* object key exists in both objects */ + if (!compare_json(from_child, to_child, case_sensitive)) + { + /* not identical --> generate a patch */ + cJSON_AddItemToObject(patch, to_child->string, cJSONUtils_GenerateMergePatch(from_child, to_child)); + } + + /* next key in the object */ + from_child = from_child->next; + to_child = to_child->next; + } + } + if (patch->child == NULL) + { + /* no patch generated */ + cJSON_Delete(patch); + return NULL; + } + + return patch; +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to) +{ + return generate_merge_patch(from, to, false); +} + +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to) +{ + return generate_merge_patch(from, to, true); +} diff --git a/source/app/common/cJSON/cJSON_Utils.h b/source/app/common/cJSON/cJSON_Utils.h new file mode 100755 index 0000000..03ec10c --- /dev/null +++ b/source/app/common/cJSON/cJSON_Utils.h @@ -0,0 +1,74 @@ +/* + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "cJSON.h" + +/* Implement RFC6901 (https://tools.ietf.org/html/rfc6901) JSON Pointer spec. */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointer(cJSON * const object, const char *pointer); +CJSON_PUBLIC(cJSON *) cJSONUtils_GetPointerCaseSensitive(cJSON * const object, const char *pointer); + +/* Implement RFC6902 (https://tools.ietf.org/html/rfc6902) JSON Patch spec. */ +/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatches(cJSON * const from, cJSON * const to); +CJSON_PUBLIC(cJSON *) cJSONUtils_GeneratePatchesCaseSensitive(cJSON * const from, cJSON * const to); +/* Utility for generating patch array entries. */ +CJSON_PUBLIC(void) cJSONUtils_AddPatchToArray(cJSON * const array, const char * const operation, const char * const path, const cJSON * const value); +/* Returns 0 for success. */ +CJSON_PUBLIC(int) cJSONUtils_ApplyPatches(cJSON * const object, const cJSON * const patches); +CJSON_PUBLIC(int) cJSONUtils_ApplyPatchesCaseSensitive(cJSON * const object, const cJSON * const patches); + +/* +// Note that ApplyPatches is NOT atomic on failure. To implement an atomic ApplyPatches, use: +//int cJSONUtils_AtomicApplyPatches(cJSON **object, cJSON *patches) +//{ +// cJSON *modme = cJSON_Duplicate(*object, 1); +// int error = cJSONUtils_ApplyPatches(modme, patches); +// if (!error) +// { +// cJSON_Delete(*object); +// *object = modme; +// } +// else +// { +// cJSON_Delete(modme); +// } +// +// return error; +//} +// Code not added to library since this strategy is a LOT slower. +*/ + +/* Implement RFC7386 (https://tools.ietf.org/html/rfc7396) JSON Merge Patch spec. */ +/* target will be modified by patch. return value is new ptr for target. */ +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatch(cJSON *target, const cJSON * const patch); +CJSON_PUBLIC(cJSON *) cJSONUtils_MergePatchCaseSensitive(cJSON *target, const cJSON * const patch); +/* generates a patch to move from -> to */ +/* NOTE: This modifies objects in 'from' and 'to' by sorting the elements by their key */ +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatch(cJSON * const from, cJSON * const to); +CJSON_PUBLIC(cJSON *) cJSONUtils_GenerateMergePatchCaseSensitive(cJSON * const from, cJSON * const to); + +/* Given a root object and a target object, construct a pointer from one to the other. */ +CJSON_PUBLIC(char *) cJSONUtils_FindPointerFromObjectTo(const cJSON * const object, const cJSON * const target); + +/* Sorts the members of the object into alphabetical order. */ +CJSON_PUBLIC(void) cJSONUtils_SortObject(cJSON * const object); +CJSON_PUBLIC(void) cJSONUtils_SortObjectCaseSensitive(cJSON * const object); diff --git a/source/app/common/common_app_def.h b/source/app/common/common_app_def.h new file mode 100644 index 0000000..7095432 --- /dev/null +++ b/source/app/common/common_app_def.h @@ -0,0 +1,24 @@ +/*! @file common_app_def.h + * + * @brief Common defines for all sample applications + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMON_APP_DEFS_H +#define COMMON_APP_DEFS_H + +#define MAX_STDOUT_LINE 100 + +#endif diff --git a/source/app/gpr_tools/CMakeLists.txt b/source/app/gpr_tools/CMakeLists.txt new file mode 100644 index 0000000..ecf9828 --- /dev/null +++ b/source/app/gpr_tools/CMakeLists.txt @@ -0,0 +1,69 @@ +# executable +set( EXE_NAME gpr_tools ) + +# get source and include files +file( GLOB GPRTOOLS_SRC_FILES "*.c" "*.cpp" ) +file( GLOB GPRTOOLS_INC_FILES "*.h" "../common/*.h" ) + +# add include files from other folders +include_directories( "../common" ) +include_directories( "../common/cJSON" ) +include_directories( "../common/argument_parser" ) +include_directories( "../common/TinyJPEG" ) +include_directories( "../../lib/common/public" ) +include_directories( "../../lib/vc5_common" ) + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + include_directories( "../../lib/vc5_decoder" ) + add_definitions("-DGPR_READING=1") +else(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + add_definitions("-DGPR_READING=0") +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + include_directories( "../../lib/vc5_encoder" ) + add_definitions("-DGPR_WRITING=1") +else(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + add_definitions("-DGPR_WRITING=0") +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + +include_directories( "../../lib/md5_lib" ) +include_directories( "../../lib/dng_sdk" ) +include_directories( "../../lib/gpr_sdk/public" ) + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + include_directories( "../../lib/tiny_jpeg" ) + add_definitions("-DGPR_JPEG_AVAILABLE=1") +else(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + add_definitions("-DGPR_JPEG_AVAILABLE=0") +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + +# add executable +add_executable( ${EXE_NAME} ${GPRTOOLS_SRC_FILES} ${GPRTOOLS_INC_FILES} ) + +# Linked libraries +target_link_libraries( ${EXE_NAME} gpr_sdk ) + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + target_link_libraries( ${EXE_NAME} tiny_jpeg ) +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + +target_link_libraries( ${EXE_NAME} dng_sdk xmp_core ) + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + target_link_libraries( ${EXE_NAME} vc5_decoder ) +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + target_link_libraries( ${EXE_NAME} vc5_encoder ) +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + +target_link_libraries( ${EXE_NAME} vc5_common common md5_lib expat_lib cJSON argument_parser ) + +# In order to use Carbon API, define qEnableCarbon in gpr_platform.h and uncomment code below +# if (APPLE) +# target_link_libraries( ${EXE_NAME} "-framework Carbon" ) +# endif (APPLE) + +# set the folder where to place the projects +set_target_properties( ${EXE_NAME} PROPERTIES FOLDER app ) diff --git a/source/app/gpr_tools/gpr_parse_utils.cpp b/source/app/gpr_tools/gpr_parse_utils.cpp new file mode 100755 index 0000000..c31ca7d --- /dev/null +++ b/source/app/gpr_tools/gpr_parse_utils.cpp @@ -0,0 +1,545 @@ +/*! @file gpr_parse_utils.cpp + * + * @brief Parsing utilities for gpr_tools + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gpr_parse_utils.h" +#include "stdcpp_utils.h" + +#include "cJSON.h" + +#include "dng_stream.h" +#include "dng_misc_opcodes.h" +#include "dng_gain_map.h" + +#define MAX_BUF_SIZE 16000 + +void parse_gps_info( cJSON* pGpsInfo, gpr_gps_info& exif_info ) +{ + exif_info.gps_info_valid = false; +} + +void parse_exif_info( cJSON* pExifInfo, gpr_exif_info& exif_info ) +{ + cJSON* pJSON = pExifInfo->child; + + strcpy( exif_info.camera_make, pJSON->valuestring ); + pJSON = pJSON->next; + + strcpy( exif_info.camera_model, pJSON->valuestring ); + pJSON = pJSON->next; + + strcpy( exif_info.camera_serial, pJSON->valuestring ); + pJSON = pJSON->next; + + strcpy( exif_info.software_version, pJSON->valuestring ); + pJSON = pJSON->next; + + strcpy( exif_info.user_comment, pJSON->valuestring ); + pJSON = pJSON->next; + + strcpy( exif_info.image_description, pJSON->valuestring ); + pJSON = pJSON->next; + + exif_info.exposure_time.numerator = pJSON->child->valueint; + exif_info.exposure_time.denominator = pJSON->child->next->valueint; + pJSON = pJSON->next; + + exif_info.f_stop_number.numerator = pJSON->child->valueint; + exif_info.f_stop_number.denominator = pJSON->child->next->valueint; + pJSON = pJSON->next; + + exif_info.aperture.numerator = pJSON->child->valueint; + exif_info.aperture.denominator = pJSON->child->next->valueint; + pJSON = pJSON->next; + + exif_info.exposure_program = (gpr_exposure_program)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.iso_speed_rating = pJSON->valueint; + pJSON = pJSON->next; + +// strcpy( exif_info.date_time_original, pJSON->valuestring ); + pJSON = pJSON->next; + +// strcpy( exif_info.date_time_digitized, pJSON->valuestring ); + pJSON = pJSON->next; + + exif_info.exposure_bias.numerator = pJSON->child->valueint; + exif_info.exposure_bias.denominator = pJSON->child->next->valueint; + pJSON = pJSON->next; + + exif_info.light_source = (gpr_light_source)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.flash = (gpr_flash)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.focal_length.numerator = pJSON->child->valueint; + exif_info.focal_length.denominator = pJSON->child->next->valueint; + pJSON = pJSON->next; + + exif_info.sharpness = (gpr_sharpness)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.saturation = pJSON->valueint; + pJSON = pJSON->next; + + exif_info.gain_control = (gpr_gain_control)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.contrast = (gpr_contrast)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.scene_capture_type = (gpr_scene_capture_type)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.exposure_mode = (gpr_exposure_mode)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.focal_length_in_35mm_film = pJSON->valueint; + pJSON = pJSON->next; + + exif_info.digital_zoom.numerator = pJSON->child->valueint; + exif_info.digital_zoom.denominator = pJSON->child->next->valueint; + pJSON = pJSON->next; + + exif_info.white_balance = (gpr_white_balance)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.scene_type = (gpr_scene_type)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.file_source = (gpr_file_source)pJSON->valueint; + pJSON = pJSON->next; + + exif_info.sensing_method = (gpr_sensing_method)pJSON->valueint; + pJSON = pJSON->next; + + parse_gps_info( pJSON, exif_info.gps_info ); +} + +void parse_profile_info( cJSON* pProfileInfo, gpr_profile_info& profile_info ) +{ + cJSON* pJSON = pProfileInfo->child; + + profile_info.compute_color_matrix = pJSON->valueint > 0 ? true : false; + pJSON = pJSON->next; + + profile_info.matrix_weighting = pJSON->valuedouble; + pJSON = pJSON->next; + + + { + cJSON* child = pJSON->child; + + profile_info.wb1[0] = child->valuedouble; + child = child->next; + + profile_info.wb1[1] = child->valuedouble; + child = child->next; + + profile_info.wb1[2] = child->valuedouble; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + profile_info.wb2[0] = child->valuedouble; + child = child->next; + + profile_info.wb2[1] = child->valuedouble; + child = child->next; + + profile_info.wb2[2] = child->valuedouble; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + profile_info.cam_to_srgb_1[0][0] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[0][1] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[0][2] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[1][0] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[1][1] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[1][2] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[2][0] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[2][1] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_1[2][2] = child->valuedouble; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + profile_info.cam_to_srgb_2[0][0] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[0][1] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[0][2] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[1][0] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[1][1] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[1][2] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[2][0] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[2][1] = child->valuedouble; + child = child->next; + + profile_info.cam_to_srgb_2[2][2] = child->valuedouble; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + profile_info.color_matrix_1[0][0] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[0][1] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[0][2] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[1][0] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[1][1] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[1][2] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[2][0] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[2][1] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_1[2][2] = child->valuedouble; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + profile_info.color_matrix_2[0][0] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[0][1] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[0][2] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[1][0] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[1][1] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[1][2] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[2][0] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[2][1] = child->valuedouble; + child = child->next; + + profile_info.color_matrix_2[2][2] = child->valuedouble; + + pJSON = pJSON->next; + } + + profile_info.illuminant1 = pJSON->valueint; + pJSON = pJSON->next; + + profile_info.illuminant2 = pJSON->valueint; +} + +void parse_tuning_info( cJSON* pTuningInfo, gpr_tuning_info& tuning_info ) +{ + cJSON* pJSON = pTuningInfo->child; + + tuning_info.orientation = (GPR_ORIENTATION)pJSON->valueint; + pJSON = pJSON->next; + + { + cJSON* child = pJSON->child; + tuning_info.static_black_level.r_black = child->valueint; + child = child->next; + + tuning_info.static_black_level.g_r_black = child->valueint; + child = child->next; + + tuning_info.static_black_level.g_b_black = child->valueint; + child = child->next; + + tuning_info.static_black_level.b_black = child->valueint; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + tuning_info.dgain_saturation_level.level_red = child->valueint; + child = child->next; + + tuning_info.dgain_saturation_level.level_green_even = child->valueint; + child = child->next; + + tuning_info.dgain_saturation_level.level_green_odd = child->valueint; + child = child->next; + + tuning_info.dgain_saturation_level.level_blue = child->valueint; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + tuning_info.wb_gains.r_gain = (float_t)child->valuedouble; + child = child->next; + + tuning_info.wb_gains.g_gain = (float_t)child->valuedouble; + child = child->next; + + tuning_info.wb_gains.b_gain = (float_t)child->valuedouble; + + pJSON = pJSON->next; + } + + { + cJSON* child = pJSON->child; + tuning_info.ae_info.iso_value = child->valueint; + child = child->next; + + tuning_info.ae_info.shutter_time = child->valueint; + + pJSON = pJSON->next; + } + + tuning_info.noise_scale = pJSON->valuedouble; + pJSON = pJSON->next; + + tuning_info.noise_offset = pJSON->valuedouble; + pJSON = pJSON->next; + + tuning_info.warp_red_coefficient = pJSON->valuedouble; + pJSON = pJSON->next; + + tuning_info.warp_blue_coefficient = pJSON->valuedouble; + pJSON = pJSON->next; + + if( pJSON->child ) + { + cJSON* size = pJSON->child; + int buffer_size = size->valueint; + + tuning_info.gain_map.size = buffer_size; + + cJSON* channel = size->next; + + int channel_index = 0; + while( channel && channel_index < 4 && buffer_size > 0 ) + { + cJSON* child = channel->child; + + int version = child->valueint; + child = child->next; + + int flags = child->valueint; + child = child->next; + + int bytes = child->valueint; + child = child->next; + + char gain_map_buffer[MAX_BUF_SIZE]; + + tuning_info.gain_map.buffers[channel_index] = (char*)malloc( buffer_size ); + + dng_stream gain_map_stream ( gain_map_buffer, buffer_size ); + + gain_map_stream.Put_uint32( version ); + gain_map_stream.Put_uint32( flags ); + gain_map_stream.Put_uint32( bytes ); + + { + cJSON* _child = child->child; + dng_rect rect; + rect.t = _child->valueint; + _child = _child->next; + + rect.l = _child->valueint; + _child = _child->next; + + rect.b = _child->valueint; + _child = _child->next; + + rect.r = _child->valueint; + + dng_area_spec area_spec(rect, 0, 1, 2, 2); + area_spec.PutData (gain_map_stream); + + child = child->next; + } + + + dng_point points; + + { + cJSON* _child = child->child; + + points.h = _child->valueint; + _child = _child->next; + + points.v = _child->valueint; + + child = child->next; + } + + dng_point_real64 spacing; + + { + cJSON* _child = child->child; + + spacing.h = _child->valuedouble; + _child = _child->next; + + spacing.v = _child->valuedouble; + + child = child->next; + } + + dng_point_real64 origin; + + { + cJSON* _child = child->child; + + origin.h = _child->valuedouble; + _child = _child->next; + + origin.v = _child->valuedouble; + + child = child->next; + } + + dng_gain_map gain_map( gDefaultDNGMemoryAllocator, points, spacing, origin, 1 ); + + cJSON* _child = child->child; + for (int row = 0; row < points.v; row++) + { + for (int col = 0; col < points.h; col++) + { + gain_map.Entry (row, col, 0) = (float_t)_child->valuedouble; + _child = _child->next; + } + } + + gain_map.PutStream( gain_map_stream ); + + memcpy( tuning_info.gain_map.buffers[channel_index], gain_map_buffer, buffer_size ); + + channel = channel->next; + + channel_index++; + } + } + + pJSON = pJSON->next; + + tuning_info.pixel_format = (GPR_PIXEL_FORMAT)pJSON->valueint; +} + +int gpr_parameters_parse( gpr_parameters* parameters, const char* input_file_path ) +{ + gpr_buffer buffer; + + if( read_from_file( &buffer, input_file_path, malloc, free) ) + { + return -2; + } + + const char* return_parse_end; + + cJSON* pRoot = cJSON_ParseWithOpts( (const char*)buffer.buffer, &return_parse_end, 0 ); + + if( pRoot == NULL ) + { + printf( "Error parsing %s \n", input_file_path ); + printf( "Error: %s", return_parse_end ); + return -1; + } + + cJSON* pJSON = pRoot->child; + + parameters->input_width = pJSON->valueint; + pJSON = pJSON->next; + + parameters->input_height = pJSON->valueint; + pJSON = pJSON->next; + + parameters->input_pitch = pJSON->valueint; + pJSON = pJSON->next; + + parameters->fast_encoding = pJSON->valueint > 0 ? true : false; + pJSON = pJSON->next; + + parameters->gpmf_payload.size = pJSON->valueint; + pJSON = pJSON->next; + + parse_exif_info( pJSON, parameters->exif_info ); + pJSON = pJSON->next; + + parse_profile_info( pJSON, parameters->profile_info ); + pJSON = pJSON->next; + + parse_tuning_info( pJSON, parameters->tuning_info ); + + free( buffer.buffer ); + + return 0; +} diff --git a/source/app/gpr_tools/gpr_parse_utils.h b/source/app/gpr_tools/gpr_parse_utils.h new file mode 100755 index 0000000..b4ebdaa --- /dev/null +++ b/source/app/gpr_tools/gpr_parse_utils.h @@ -0,0 +1,34 @@ +/*! @file gpr_parse_utils.h + * + * @brief Parsing utilities for gpr_tools + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_PARSE_UTILS_H +#define GPR_PARSE_UTILS_H + +#include "gpr.h" + +#ifdef __cplusplus +extern "C" { +#endif + + int gpr_parameters_parse( gpr_parameters* parameters, const char* input_file_path ); + +#ifdef __cplusplus +} +#endif + +#endif // GPR_PARSE_UTILS_H diff --git a/source/app/gpr_tools/gpr_print_utils.cpp b/source/app/gpr_tools/gpr_print_utils.cpp new file mode 100755 index 0000000..610db75 --- /dev/null +++ b/source/app/gpr_tools/gpr_print_utils.cpp @@ -0,0 +1,541 @@ +/*! @file gpr_print_utils.cpp + * + * @brief Printing utilities for gpr_tools + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gpr_print_utils.h" + +#include +#include +#include + +using namespace std; + +#include "dng_stream.h" +#include "dng_misc_opcodes.h" +#include "dng_gain_map.h" + +uint32 spaces = 0; + +ostream& operator<<(ostream& output, const gpr_signed_rational& x) +{ + output << "[" << x.numerator << "," << x.denominator << "]"; + + return output; +} + +ostream& operator<<(ostream& output, const gpr_unsigned_rational& x) +{ + output << "[" << x.numerator << "," << x.denominator << "]"; + + return output; +} + +ostream& operator<<(ostream& output, const gpr_date_and_time& x) +{ + output << "\"" << x.year << "-" << x.month << "-" << x.day << " " << x.hour << ":" << x.minute << ":" << x.second << "\""; + + return output; +} + +ostream& operator<<(ostream& output, const dng_area_spec& x) +{ + dng_rect area = x.Area(); + + output << "{ \"top\" : " << area.t << ", \"left\" : " << area.l << ", \"bottom\" : " << area.b << ", \"right\" : " << area.r << ", \"row_pitch\" : " << x.RowPitch() << ", \"col_pitch\" : " << x.ColPitch() << " }"; + + return output; +} + +ostream& operator<<(ostream& output, const dng_point& x) +{ + output << "{ \"h\" : " << x.h << ", \"v\" : " << x.v << " }"; + + return output; +} + +ostream& operator<<(ostream& output, const dng_point_real64& x) +{ + output << "{ \"h\" : " << x.h << ", \"v\" : " << x.v << " }"; + + return output; +} + +void start_tag( const string& tag_id, ostream& output ) +{ + output << "{" << endl; + spaces += 2; +} + +void end_tag( const string& tag_id, ostream& output ) +{ + spaces -= 2; + output << string( spaces, ' ' ).c_str() << "}"; +} + +template +void print_val(ostream& output, string tag, T x, int N = 0, bool last = false) +{ + if( last ) + output << string( spaces, ' ' ).c_str() << "\"" << tag.c_str() << "\": " << x << "" << endl; + else + output << string( spaces, ' ' ).c_str() << "\"" << tag.c_str() << "\": " << x << "," << endl; +} + +template<> +void print_val(ostream& output, string tag, const char* x, int N, bool last) +{ + if( last ) + output << string( spaces, ' ' ).c_str() << "\"" << tag.c_str() << "\": " << "\"" << x << "\"" << "" << endl; + else + output << string( spaces, ' ' ).c_str() << "\"" << tag.c_str() << "\": " << "\"" << x << "\"" << "," << endl; +} + +template<> +void print_val(ostream& output, string tag, const double* x, int N, bool last) +{ + output << string( spaces, ' ' ).c_str() << "\"" << tag.c_str() << "\": " << "["; + + for (int i = 0; i < N; i++) + { + if( i < N - 1 ) + output << x[i] << ","; + else + output << x[i]; + } + + if( last ) + output << "]" << "" << endl; + else + output << "]" << "," << endl; +} + +template<> +void print_val(ostream& output, string tag, const gpr_unsigned_rational* x, int N, bool last) +{ + output << string( spaces, ' ' ).c_str() << "\"" << tag.c_str() << "\": " << "["; + + for (int i = 0; i < N; i++) + { + if( i < N - 1 ) + output << x[i] << ","; + else + output << x[i]; + } + + if( last ) + output << "]" << "" << endl; + else + output << "]" << "," << endl; +} + +template +void print_val(ostream& output, string tag, T x[N][N]) +{ + output << string( spaces, ' ' ) << "\"" << tag << "\": " << "["; + output << x[0][0] << ","; + output << x[0][1] << ","; + output << x[0][2] << ","; + output << x[1][0] << ","; + output << x[1][1] << ","; + output << x[1][2] << ","; + output << x[2][0] << ","; + output << x[2][1] << ","; + output << x[2][2]; + output << "]"; + + output << "," << endl; +} + +ostream& operator<<(ostream& output, const gpr_gain_map& x) +{ + start_tag( "gain_map", output ); + + if( x.size > 0 ) + { + print_val( output, "size", x.size ); + + for (int i = 0; i < 4; i++) + { + dng_stream gain_map_stream (x.buffers[i], x.size); + + output << string( spaces, ' ' ).c_str() << "\"" << "channel_" << i << "\": "; + start_tag( "channel", output ); + + print_val( output, "version", gain_map_stream.Get_uint32() ); + + print_val( output, "flags", gain_map_stream.Get_uint32() ); + + print_val( output, "bytes", gain_map_stream.Get_uint32() ); + + dng_area_spec area_spec; + area_spec.GetData (gain_map_stream); + + print_val( output, "area", area_spec ); + + AutoPtr gain_map; + + gain_map.Reset (dng_gain_map::GetStream (gain_map_stream, gDefaultDNGMemoryAllocator)); + + dng_point points = gain_map.Get()->Points(); + dng_point_real64 spacing = gain_map.Get()->Spacing(); + dng_point_real64 origin = gain_map.Get()->Origin(); + + print_val( output, "points", points ); + print_val( output, "spacing", spacing ); + print_val( output, "origin", origin ); + + output << string( spaces, ' ' ).c_str() << "\"" << "values" << "\": ["; + + for (int row = 0; row < points.v; row++) + { + for (int col = 0; col < points.h; col++) + { + output << gain_map->Entry (row, col, 0); + + if( row == points.v - 1 && col == points.h - 1 ) + output << " "; + else + output << ", "; + } + } + output << "] " << endl; + + end_tag( "channel", output ); + + if( i < 3 ) + output << ", " << endl; + } + } + + end_tag( "gain_map", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_gps_info& x) +{ + start_tag( "gps_info", output ); + + if( x.gps_info_valid ) + { + print_val( output, "gps_info_valid", x.gps_info_valid ); + + print_val( output, "version_id", x.version_id ); + + print_val( output, "latitude_ref", x.latitude_ref ); + + print_val( output, "latitude", x.latitude, 3 ); + + print_val( output, "longitude_ref", x.longitude_ref ); + + print_val( output, "longitude", x.longitude, 3 ); + + print_val( output, "altitude_ref", (uint32)x.altitude_ref ); + + print_val( output, "altitude", x.altitude ); + + print_val( output, "time_stamp", x.time_stamp ); + + print_val( output, "satellites", x.satellites ); + + print_val( output, "status", x.status ); + + print_val( output, "dop", x.dop ); + + print_val( output, "speed_ref", x.speed_ref ); + + print_val( output, "speed", x.speed ); + + print_val( output, "track_ref", x.track_ref ); + + print_val( output, "track", x.track ); + + print_val( output, "img_direction_ref", x.img_direction_ref ); + + print_val( output, "img_direction", x.img_direction ); + + print_val( output, "map_datum", x.map_datum ); + + print_val( output, "dest_latitude_ref", x.dest_latitude_ref ); + + print_val( output, "dest_latitude", x.dest_latitude ); + + print_val( output, "dest_longitude_ref", x.dest_longitude_ref ); + + print_val( output, "dest_longitude", x.dest_longitude ); + + print_val( output, "dest_bearing_ref", x.dest_bearing_ref ); + + print_val( output, "dest_bearing", x.dest_bearing ); + + print_val( output, "dest_distance_ref", x.dest_distance_ref ); + + print_val( output, "dest_distance", x.dest_distance ); + + print_val( output, "processing_method", x.processing_method ); + + print_val( output, "area_information", x.area_information ); + + print_val( output, "date_stamp", x.date_stamp ); + + print_val( output, "differential", x.differential, 0, true ); + } + else + { + print_val( output, "gps_info_valid", x.gps_info_valid, 0, true ); + } + + end_tag( "gps_info", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_exif_info& x) +{ + start_tag( "exif_info", output ); + + print_val( output, "camera_make", x.camera_make ); + + print_val( output, "camera_model", x.camera_model ); + + print_val( output, "camera_serial", x.camera_serial ); + + print_val( output, "software_version", x.software_version ); + + print_val( output, "user_comment", x.user_comment ); + + { + string str_image_description = x.image_description; + std::replace( str_image_description.begin(), str_image_description.end(), '\\', '/'); + print_val( output, "image_description", str_image_description.c_str() ); + } + + print_val( output, "exposure_time", x.exposure_time ); + + print_val( output, "f_stop_number", x.f_stop_number ); + + print_val( output, "aperture", x.aperture ); + + print_val( output, "exposure_program", x.exposure_program ); + + print_val( output, "iso_speed_rating", x.iso_speed_rating ); + + print_val( output, "date_time_original", x.date_time_original ); + + print_val( output, "date_time_digitized", x.date_time_digitized ); + + print_val( output, "exposure_bias", x.exposure_bias ); + + print_val( output, "light_source", x.light_source ); + + print_val( output, "flash", x.flash ); + + print_val( output, "focal_length", x.focal_length ); + + print_val( output, "sharpness", x.sharpness ); + + print_val( output, "saturation", x.saturation ); + + print_val( output, "gain_control", x.gain_control ); + + print_val( output, "contrast", x.contrast ); + + print_val( output, "scene_capture_type", x.scene_capture_type ); + + print_val( output, "exposure_mode", x.exposure_mode ); + + print_val( output, "focal_length_in_35mm_film", x.focal_length_in_35mm_film ); + + print_val( output, "digital_zoom", x.digital_zoom ); + + print_val( output, "white_balance", x.white_balance ); + + print_val( output, "scene_type", x.scene_type ); + + print_val( output, "file_source", x.file_source ); + + print_val( output, "sensing_method", x.sensing_method ); + + print_val( output, "gps_info", x.gps_info, 0, true ); + + end_tag( "exif_info", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_profile_info& x) +{ + start_tag( "profile_info", output ); + + print_val( output, "compute_color_matrix", x.compute_color_matrix ); + + print_val( output, "matrix_weighting", x.matrix_weighting ); + + print_val( output, "wb1", x.wb1, 3, false ); + + print_val( output, "wb2", x.wb2, 3, false ); + + print_val( output, "cam_to_srgb_1", (const double*)x.cam_to_srgb_1, 9, false ); + + print_val( output, "cam_to_srgb_2", (const double*)x.cam_to_srgb_2, 9, false ); + + print_val( output, "color_matrix_1", (const double*)x.color_matrix_1, 9, false ); + + print_val( output, "color_matrix_2", (const double*)x.color_matrix_2, 9, false ); + + print_val( output, "illuminant1", x.illuminant1, 0, false ); + + print_val( output, "illuminant2", x.illuminant2, 0, true ); + + end_tag( "profile_info", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_static_black_level& x) +{ + start_tag( "static_black_level", output ); + + print_val( output, "r_black", x.r_black ); + + print_val( output, "g_r_black", x.g_r_black ); + + print_val( output, "g_b_black", x.g_b_black ); + + print_val( output, "b_black", x.b_black, 0, true ); + + end_tag( "static_black_level", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_saturation_level& x) +{ + start_tag( "dgain_saturation_level", output ); + + print_val( output, "level_red", x.level_red ); + + print_val( output, "level_green_even", x.level_green_even ); + + print_val( output, "level_green_odd", x.level_green_odd ); + + print_val( output, "level_blue", x.level_blue, 0, true ); + + end_tag( "dgain_saturation_level", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_white_balance_gains& x) +{ + start_tag( "wb_gains", output ); + + print_val( output, "r_gain", x.r_gain ); + + print_val( output, "g_gain", x.g_gain ); + + print_val( output, "b_gain", x.b_gain, 0, true ); + + end_tag( "wb_gains", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_auto_exposure_info& x) +{ + start_tag( "ae_info", output ); + + print_val( output, "iso_value", x.iso_value ); + + print_val( output, "shutter_time", x.shutter_time, 0, true ); + + end_tag( "ae_info", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_tuning_info& x) +{ + start_tag( "tuning_info", output ); + + print_val( output, "orientation", x.orientation ); + + print_val( output, "static_black_level", x.static_black_level ); + + print_val( output, "dgain_saturation_level", x.dgain_saturation_level ); + + print_val( output, "wb_gains", x.wb_gains ); + + print_val( output, "ae_info", x.ae_info ); + + print_val( output, "noise_scale", x.noise_scale ); + + print_val( output, "noise_offset", x.noise_offset ); + + print_val( output, "warp_red_coefficient", x.warp_red_coefficient ); + + print_val( output, "warp_blue_coefficient", x.warp_blue_coefficient ); + + print_val( output, "gain_map", x.gain_map ); + + print_val( output, "pixel_format", x.pixel_format, 0, true ); + + end_tag( "tuning_info", output ); + + return output; +} + +ostream& operator<<(ostream& output, const gpr_parameters& x) +{ + print_val( output, "input_width", x.input_width ); + + print_val( output, "input_height", x.input_height ); + + print_val( output, "input_pitch", x.input_pitch ); + + print_val( output, "fast_encoding", x.fast_encoding ); + + // print_val( output, "gpmf_payload_buffer", x.gpmf_payload.buffer ); + + print_val( output, "gpmf_payload_size", x.gpmf_payload.size ); + + print_val( output, "exif_info", x.exif_info ); + + print_val( output, "profile_info", x.profile_info ); + + print_val( output, "tuning_info", x.tuning_info, 0, true ); + + return output; +} + +int gpr_parameters_print( const gpr_parameters* parameters, const char* output_file_path ) +{ + ofstream output; + ostream* output_ref = &cout; + + if( output_file_path ) + { + output.open (output_file_path); + output_ref = &output; + } + + start_tag( "", *output_ref ); + *output_ref << *parameters; + end_tag( "", *output_ref ); + + return 0; +} diff --git a/source/app/gpr_tools/gpr_print_utils.h b/source/app/gpr_tools/gpr_print_utils.h new file mode 100755 index 0000000..34dc63a --- /dev/null +++ b/source/app/gpr_tools/gpr_print_utils.h @@ -0,0 +1,34 @@ +/*! @file gpr_print_utils.h + * + * @brief Printing utilities for gpr_tools + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_PRINT_UTILS_H +#define GPR_PRINT_UTILS_H + +#include "gpr.h" + +#ifdef __cplusplus +extern "C" { +#endif + + int gpr_parameters_print( const gpr_parameters* parameters, const char* output_file_path ); + +#ifdef __cplusplus +} +#endif + +#endif // GPR_PRINT_UTILS_H diff --git a/source/app/gpr_tools/main.cpp b/source/app/gpr_tools/main.cpp new file mode 100755 index 0000000..e182028 --- /dev/null +++ b/source/app/gpr_tools/main.cpp @@ -0,0 +1,203 @@ +/*! @file main.cpp + * + * @brief Main program file for the gpr_tools. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include "argument_parser.h" + +#include "gpr.h" +#include "gpr_buffer.h" +#include "gpr_print_utils.h" +#include "main_c.h" + +#include "common_app_def.h" + +using namespace std; + +class my_argument_parser : public argument_parser +{ +protected: + + bool help; + + bool verbose; + +public: + + bool dump_gpr_parameters; + + string jpg_preview_file_path; + + int jpg_preview_file_width; + + int jpg_preview_file_height; + + int input_width; + + int input_height; + + int input_pitch; + + int input_skip_rows; + + string input_pixel_format; + + string input_file_path; + + string gpmf_file_path; + + string rgb_file_resolution; + + int rgb_file_bits; + + string output_file_path; + + string apply_gpr_parameters; + +public: + + bool get_verbose() { return verbose; } + + bool get_help() { return help; } + + void set_options() + { + command_options.addOptions() + /* long and short name */ /* variable to update */ /* default value */ /* help text */ + ("help", help, false, "Prints this help text") + + ("verbose", verbose, false, "Verbosity of the output") + + ("JpgPreviewFilePath,P", jpg_preview_file_path, string(""), "Preview jpg file path") + ("JpgPreviewFileWidth,W", jpg_preview_file_width, 0, "Preview jpg file width") + ("JpgPreviewFileHeight,H", jpg_preview_file_height, 0, "Preview jpg file height") + + ("DumpGprParameters,d", dump_gpr_parameters, false, "Dump GPR parameters to standard output") + + ("InputSkipRows,s", input_skip_rows, 0, "Input image rows to skip") + + ("InputFilePath,i", input_file_path, string(""), "Input file path.\n(files types: GPR, DNG, RAW)") + + ("InputWidth,w", input_width, 4000, "Input image width in pixel samples [4000]") + + ("InputHeight,h", input_height, 3000, "Input image height in pixel samples [3000]") + + ("InputPitch,p", input_pitch, 8000, "Input image pitch in bytes [8000]") + + ("InputPixelFormat,x", input_pixel_format, string("rggb14"), "Input pixel format \n(rggb12, rggb12p, [rggb14], gbrg12, gbrg12p)") + + ("ApplyGprParameters,a", apply_gpr_parameters, string(""), "Parameters to use for GPR or DNG file.") + + ("GPMFFilePath,g", gpmf_file_path, string(""), "GPMF file path") + + ("RgbFileResolution,r", rgb_file_resolution, string(""), "Output RGB resolution \n[1:1, 2:1, 4:1, 8:1. 16:1]") + ("RgbFileBits,b", rgb_file_bits, 8, "Output RGB bits [8]") + + ("OutputFilePath,o", output_file_path, string(""), "Output file path.\n(files types: GPR, DNG, PPM, RAW, JPG)"); + ; + } +}; + +int dng_dump(const char* input_file_path) +{ + gpr_allocator allocator; + allocator.Alloc = malloc; + allocator.Free = free; + + gpr_buffer input_buffer = { NULL, 0 }; + + gpr_parameters params; + + gpr_parameters_set_defaults(¶ms); + + if( read_from_file( &input_buffer, input_file_path, allocator.Alloc, allocator.Free ) != 0 ) + { + return -1; + } + + int success = gpr_parse_metadata( &allocator, &input_buffer, ¶ms ); + + if( success ) + { + gpr_parameters_print( ¶ms, NULL ); + } + + return 0; +} + +int main(int argc, char *argv []) +{ + my_argument_parser args; + + + char zerotag[MAX_STDOUT_LINE]; + sprintf(zerotag, "[%5d-ms] ", 0); + + char line[MAX_STDOUT_LINE]; + sprintf( line, "GPR Tools Version %d.%d.%d [%s @ %s] ", GPR_VERSION_MAJOR, GPR_VERSION_MINOR, GPR_VERSION_REVISION, GIT_BRANCH, GIT_COMMIT_HASH ); + + if( args.parse(argc, argv, line, zerotag) ) + { + printf("\n"); + printf("-- Example Commnads (please see data/tests/run_tests.sh for more examples) --\n"); + printf("GPR to DNG: \n"); + printf(" %s -i ./data/samples/Hero6/GOPR0024.GPR -o ./data/samples/Hero6/GOPR0024.DNG \n\n", argv[0] ); + printf("GPR to RGB (PPM format in 1000x750 resolution): \n"); + printf(" %s -i ./data/samples/Hero6/GOPR0024.GPR -o ./data/samples/Hero6/GOPR0024.PPM -r 4:1 \n\n", argv[0] ); + printf("GPR to RGB (JPG format in 500x375 resolution): \n"); + printf(" %s -i ./data/samples/Hero6/GOPR0024.GPR -o ./data/samples/Hero6/GOPR0024.JPG -r 8:1 \n\n", argv[0] ); + printf("Analyze a GPR or DNG file and output metadata parameters to a file: \n"); + printf(" %s -i ./data/samples/Hero6/GOPR0024.GPR -d 1 > ./data/samples/Hero6/GOPR0024.TXT \n\n", argv[0] ); + printf("Read RAW pixel data, along with gpr parameters (from a file) and apply to an output GPR or DNG file: \n"); + printf(" %s -i ./data/samples/Hero6/GOPR0024.RAW -o ./data/samples/Hero6/GOPR0024.DNG -a ./data/samples/Hero6/GOPR0024.TXT \n\n", argv[0] ); + + return -1; + } + + if( args.dump_gpr_parameters ) + { + if( dng_dump(args.input_file_path.c_str()) != 0 ) + return -1; + } + else + { + string ext = strrchr( args.input_file_path.c_str(),'.'); + + if( args.output_file_path == string("") && ( ext == string(".GPR") || ext == string(".gpr") ) ) + { + args.output_file_path = args.input_file_path; + args.output_file_path.erase(args.output_file_path.find_last_of("."), string::npos); + + args.output_file_path = args.output_file_path + string(".DNG"); + } + } + + fprintf( stderr, "%s Input File: %s \n", zerotag, args.input_file_path.c_str() ); + fprintf( stderr, "%s Output File: %s \n", zerotag, args.output_file_path.c_str() ); + + if( args.output_file_path != "" ) + { + return dng_convert_main(args.input_file_path.c_str(), args.input_width, args.input_height, args.input_pitch, args.input_skip_rows, args.input_pixel_format.c_str(), + args.output_file_path.c_str(), args.apply_gpr_parameters.c_str(), args.gpmf_file_path.c_str(), args.rgb_file_resolution.c_str(), args.rgb_file_bits, + args.jpg_preview_file_path.c_str(), args.jpg_preview_file_width, args.jpg_preview_file_height ); + } + + return 0; +} + diff --git a/source/app/gpr_tools/main_c.c b/source/app/gpr_tools/main_c.c new file mode 100755 index 0000000..0583b01 --- /dev/null +++ b/source/app/gpr_tools/main_c.c @@ -0,0 +1,346 @@ +/*! @file main_c.c + * + * @brief Implement C conversion routines used by gpr_tools + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include + +#include "gpr.h" + +#if defined __GNUC__ +#define stricmp strcasecmp +#else +#endif // if defined __GNUC__ + +#include "main_c.h" +#include "gpr_parse_utils.h" +#include "gpr_print_utils.h" + +#if GPR_JPEG_AVAILABLE +#include "jpeg.h" +#endif + +#define MAX_FILE_PATH 256 + +typedef enum +{ + FILE_TYPE_UNKNOWN = -1, + + FILE_TYPE_RAW, + FILE_TYPE_GPR, + FILE_TYPE_DNG, + FILE_TYPE_PPM, + FILE_TYPE_JPG, + + FILE_TYPE_COUNT, + +} FILE_TYPE; + +static FILE_TYPE GetFileType( const char* file_path ) +{ + const char *extension = NULL; + + if (file_path == NULL) { + return FILE_TYPE_UNKNOWN; + } + + // Get the pathname extension + extension = strrchr(file_path, '.'); + if (extension == NULL) + { + return FILE_TYPE_UNKNOWN; + } + + if (stricmp(extension, ".raw") == 0 || stricmp(extension, ".RAW") == 0) + { + return FILE_TYPE_RAW; + } + + if (stricmp(extension, ".gpr") == 0 || stricmp(extension, ".GPR") == 0) + { + return FILE_TYPE_GPR; + } + + if (stricmp(extension, ".dng") == 0 || stricmp(extension, ".DNG") == 0 ) + { + return FILE_TYPE_DNG; + } + + if (stricmp(extension, ".ppm") == 0 || stricmp(extension, ".PPM") == 0) + { + return FILE_TYPE_PPM; + } + + if (stricmp(extension, ".jpg") == 0 || stricmp(extension, ".JPG") == 0) + { + return FILE_TYPE_JPG; + } + + return FILE_TYPE_UNKNOWN; +} + +int dng_convert_main(const char* input_file_path, unsigned int input_width, unsigned int input_height, size_t input_pitch, size_t input_skip_rows, const char* input_pixel_format, + const char* output_file_path, const char* metadata_file_path, const char* gpmf_file_path, const char* rgb_file_resolution, int rgb_file_bits, + const char* jpg_preview_file_path, int jpg_preview_file_width, int jpg_preview_file_height ) +{ + bool success; + bool write_buffer_to_file = true; + + FILE_TYPE input_file_type = GetFileType( input_file_path ); + FILE_TYPE output_file_type = GetFileType( output_file_path ); + + if( input_file_type == FILE_TYPE_UNKNOWN ) + { + printf( "Unsupported input file type" ); + return -1; + } + + if( output_file_type == FILE_TYPE_UNKNOWN ) + { + printf( "Unsupported output file type" ); + return -1; + } + + gpr_allocator allocator; + allocator.Alloc = malloc; + allocator.Free = free; + + gpr_parameters params; + gpr_parameters_set_defaults(¶ms); + + gpr_buffer input_buffer = { NULL, 0 }; + + if( read_from_file( &input_buffer, input_file_path, allocator.Alloc, allocator.Free ) != 0 ) + { + return -1; + } + + if( metadata_file_path && strcmp(metadata_file_path, "") ) + { + if( gpr_parameters_parse( ¶ms, metadata_file_path ) != 0 ) + return -1; + } + else if( input_file_type == FILE_TYPE_GPR || input_file_type == FILE_TYPE_DNG ) + { + gpr_parse_metadata( &allocator, &input_buffer, ¶ms ); + } + else + { + params.input_width = input_width; + params.input_height = input_height; + params.input_pitch = input_pitch; + + int32_t saturation_level = params.tuning_info.dgain_saturation_level.level_red; + + if( output_file_type == FILE_TYPE_GPR ) + saturation_level = (1 << 14) - 1; + else if( output_file_type == FILE_TYPE_DNG ) + saturation_level = (1 << 12) - 1; + + if( strcmp(input_pixel_format, "rggb12") == 0 ) + { + params.tuning_info.pixel_format = PIXEL_FORMAT_RGGB_12; + + if( input_pitch == -1 ) + input_pitch = input_width * 2; + } + if( strcmp(input_pixel_format, "rggb12p") == 0 ) + { + params.tuning_info.pixel_format = PIXEL_FORMAT_RGGB_12P; + + if( input_pitch == -1 ) + input_pitch = (input_width * 3 / 4) * 2; + } + else if( strcmp(input_pixel_format, "rggb14") == 0 ) + { + params.tuning_info.pixel_format = PIXEL_FORMAT_RGGB_14; + + saturation_level = (1 << 14) - 1; + + if( input_pitch == -1 ) + input_pitch = input_width * 2; + } + else if( strcmp(input_pixel_format, "gbrg12") == 0 ) + { + params.tuning_info.pixel_format = PIXEL_FORMAT_GBRG_12; + + if( input_pitch == -1 ) + input_pitch = input_width * 2; + } + else if( strcmp(input_pixel_format, "gbrg12p") == 0 ) + { + params.tuning_info.pixel_format = PIXEL_FORMAT_GBRG_12P; + + if( input_pitch == -1 ) + input_pitch = (input_width * 3 / 4) * 2; + } + + params.tuning_info.dgain_saturation_level.level_red = saturation_level; + params.tuning_info.dgain_saturation_level.level_green_even = saturation_level; + params.tuning_info.dgain_saturation_level.level_green_odd = saturation_level; + params.tuning_info.dgain_saturation_level.level_blue = saturation_level; + } + + if( gpmf_file_path != NULL && strcmp(gpmf_file_path, "") ) + { + read_from_file( ¶ms.gpmf_payload, gpmf_file_path, allocator.Alloc, allocator.Free ); + } + + gpr_buffer output_buffer = { NULL, 0 }; + + if( input_skip_rows > 0 ) + { + input_buffer.buffer = (unsigned char*)(input_buffer.buffer) + (input_skip_rows * input_pitch); + } + + gpr_buffer preview = { NULL, 0 }; + + if( strcmp(jpg_preview_file_path, "") != 0 ) + { + if( read_from_file( &preview, jpg_preview_file_path, allocator.Alloc, allocator.Free) == 0 ) + { + params.preview_image.jpg_preview = preview; + params.preview_image.preview_width = jpg_preview_file_width; + params.preview_image.preview_height = jpg_preview_file_height; + } + } + + if( input_file_type == FILE_TYPE_RAW && output_file_type == FILE_TYPE_DNG ) + { + success = gpr_convert_raw_to_dng( &allocator, ¶ms, &input_buffer, &output_buffer ); + } + else if( input_file_type == FILE_TYPE_DNG && output_file_type == FILE_TYPE_RAW ) + { + success = gpr_convert_dng_to_raw( &allocator, &input_buffer, &output_buffer ); + } + else if( input_file_type == FILE_TYPE_DNG && output_file_type == FILE_TYPE_DNG ) + { + success = gpr_convert_dng_to_dng( &allocator, ¶ms, &input_buffer, &output_buffer ); + } +#if GPR_WRITING + else if( input_file_type == FILE_TYPE_DNG && output_file_type == FILE_TYPE_GPR ) + { + success = gpr_convert_dng_to_gpr( &allocator, ¶ms, &input_buffer, &output_buffer ); + } + else if( input_file_type == FILE_TYPE_RAW && output_file_type == FILE_TYPE_GPR ) + { + success = gpr_convert_raw_to_gpr( &allocator, ¶ms, &input_buffer, &output_buffer ); + } +#endif +#if GPR_READING + else if( input_file_type == FILE_TYPE_GPR && ( output_file_type == FILE_TYPE_PPM || output_file_type == FILE_TYPE_JPG ) ) + { + gpr_rgb_buffer rgb_buffer = { NULL, 0, 0, 0 }; + + GPR_RGB_RESOLUTION rgb_resolution = GPR_RGB_RESOLUTION_DEFAULT; + + if( strcmp(rgb_file_resolution, "1:1") == 0 ) + rgb_resolution = GPR_RGB_RESOLUTION_FULL; + else if( strcmp(rgb_file_resolution, "2:1") == 0 ) + rgb_resolution = GPR_RGB_RESOLUTION_HALF; + else if( strcmp(rgb_file_resolution, "4:1") == 0 ) + rgb_resolution = GPR_RGB_RESOLUTION_QUARTER; + else if( strcmp(rgb_file_resolution, "8:1") == 0 ) + rgb_resolution = GPR_RGB_RESOLUTION_EIGHTH; + else if( strcmp(rgb_file_resolution, "16:1") == 0 ) + rgb_resolution = GPR_RGB_RESOLUTION_SIXTEENTH; + + if( output_file_type == FILE_TYPE_JPG && rgb_file_bits == 16 ) + { + printf( "Asked to output 16-bits RGB, but that is only possible in PPM format.\n"); + rgb_file_bits = 8; + } + + success = gpr_convert_gpr_to_rgb( &allocator, rgb_resolution, rgb_file_bits, &input_buffer, &rgb_buffer ); + + if( output_file_type == FILE_TYPE_PPM ) + { +#define PPM_HEADER_SIZE 100 + char header_text[PPM_HEADER_SIZE]; + + if( rgb_file_bits == 8 ) + { + // 8 bits + sprintf( header_text, "P6\n%ld %ld\n255\n", rgb_buffer.width, rgb_buffer.height ); + } + else + { + // 16 bits + sprintf( header_text, "P6\n%ld %ld\n65535\n", rgb_buffer.width, rgb_buffer.height ); + } + + output_buffer.size = rgb_buffer.size + strlen( header_text ); + output_buffer.buffer = allocator.Alloc( output_buffer.size ); + char* buffer_c = (char*)output_buffer.buffer; + + memcpy( buffer_c, header_text, strlen( header_text ) ); + memcpy( buffer_c + strlen( header_text ), rgb_buffer.buffer, rgb_buffer.size ); +#undef PPM_HEADER_SIZE + } + else if( output_file_type == FILE_TYPE_JPG ) + { + write_buffer_to_file = false; +#if GPR_JPEG_AVAILABLE + tje_encode_to_file( output_file_path, rgb_buffer.width, rgb_buffer.height, 3, rgb_buffer.buffer ); +#else + printf("JPG writing capability is disabled. You could still write to a PPM file"); +#endif + } + + allocator.Free( rgb_buffer.buffer ); + } + else if( input_file_type == FILE_TYPE_GPR && output_file_type == FILE_TYPE_DNG ) + { + success = gpr_convert_gpr_to_dng( &allocator, ¶ms, &input_buffer, &output_buffer ); + } + else if( input_file_type == FILE_TYPE_GPR && output_file_type == FILE_TYPE_RAW ) + { + success = gpr_convert_gpr_to_raw( &allocator, &input_buffer, &output_buffer ); + } +#endif + else + { + printf( "Unsupported conversion from %s to %s \n", input_file_path, output_file_path ); + return -1; + } + + if( success == 0 ) + { + printf("Conversion failed \n"); + return -1; + } + else if( write_buffer_to_file ) + { + write_to_file( &output_buffer, output_file_path ); + } + + if( input_skip_rows > 0 ) + { + input_buffer.buffer = (unsigned char*)(input_buffer.buffer) - (input_skip_rows * input_pitch); + } + + if( preview.buffer ) + { + allocator.Free( preview.buffer ); + } + + gpr_parameters_destroy(¶ms, allocator.Free); + + return 0; +} + diff --git a/source/app/gpr_tools/main_c.h b/source/app/gpr_tools/main_c.h new file mode 100755 index 0000000..a8bd308 --- /dev/null +++ b/source/app/gpr_tools/main_c.h @@ -0,0 +1,34 @@ +/*! @file main_c.h + * + * @brief Definition of C conversion routines used by gpr_tools + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MAIN_C_H +#define MAIN_C_H + +#ifdef __cplusplus +extern "C" { +#endif + + int dng_convert_main(const char* input_file_path, unsigned int input_width, unsigned int input_height, size_t input_pitch, size_t input_skip_rows, const char* input_pixel_format, + const char* output_file_path, const char* exiftool_file_path, const char* gpmf_file_path, const char* rgb_file_resolution, int rgb_file_bits, + const char* jpg_preview_file_path, int jpg_preview_file_width, int jpg_preview_file_height ); + +#ifdef __cplusplus +} +#endif + +#endif // MAIN_C_H diff --git a/source/app/gpr_tools/stdcpp_utils.h b/source/app/gpr_tools/stdcpp_utils.h new file mode 100755 index 0000000..eb7191d --- /dev/null +++ b/source/app/gpr_tools/stdcpp_utils.h @@ -0,0 +1,138 @@ +/*! @file stdcpp_utils.h + * + * @brief Implement some standard C++ routines using templates + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STDCPP_UTILS_H +#define STDCPP_UTILS_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + + std::string& ltrim(std::string& s) { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), + std::ptr_fun(std::isgraph))); + return s; + } + + std::string& rtrim(std::string& s) + { + s.erase(std::find_if(s.rbegin(), s.rend(), + std::ptr_fun(std::isgraph)).base(), s.end()); + return s; + } + + std::string& trim(std::string& s) + { + return ltrim(rtrim(s)); + } + + std::vector tokenizer( const std::string& p_pcstStr, char delim ) { + std::vector tokens; + std::stringstream mySstream( p_pcstStr ); + std::string temp; + + while( getline( mySstream, temp, delim ) ) { + tokens.push_back( temp ); + } + + return tokens; + } + + template + bool find_key( std::map hash, const std::string key, T& value ) + { + std::map::iterator it = hash.find( key.c_str() ); + if(it != hash.end()) + { + value = atoi( it->second.c_str() ); + + return true; + } + + return false; + } + + template<> + bool find_key( std::map hash, const std::string key, std::string& value ) + { + std::map::iterator it = hash.find( key.c_str() ); + if(it != hash.end()) + { + value = it->second; + + return true; + } + + return false; + } + + template + bool find_key( std::map hash, const std::string key, T& value_numerator, T& value_denominator ) + { + std::map::iterator it = hash.find( key.c_str() ); + if(it != hash.end()) + { + std::string fraction = it->second; + std::vector tokens = tokenizer( fraction, '/' ); + + value_numerator = atoi( tokens[0].c_str() ); + + if( tokens.size() == 2 ) + value_denominator = atoi( tokens[1].c_str() ); + else + value_denominator = 1; + + return true; + } + + return false; + } + + + template + static T parse_field(const char *tuning_sring, const char *field_name, const char* format_specifier, T default_val, bool* found = NULL ) + { + const char *foundStr = strstr(tuning_sring, field_name); + T ret_data; + + if (foundStr) + { + sscanf(foundStr + strlen(field_name), format_specifier, &ret_data); + + if(found) + *found = true; + + return ret_data; + } + + if(found) + *found = false; + + return default_val; + } + +#endif // STDCPP_UTILS_H diff --git a/source/app/vc5_decoder_app/CMakeLists.txt b/source/app/vc5_decoder_app/CMakeLists.txt new file mode 100644 index 0000000..4e2aa3c --- /dev/null +++ b/source/app/vc5_decoder_app/CMakeLists.txt @@ -0,0 +1,25 @@ +# executable +set( EXE_NAME vc5_decoder_app ) + +# get source and include files +file( GLOB DECODER_SRC_FILES "*.c" "*.cpp" ) +file( GLOB DECODER_INC_FILES "*.h" "../common/*.h" ) + +# add include files from other folders +include_directories( "../common" ) +include_directories( "../common/argument_parser" ) +include_directories( "../../lib/common/private" ) +include_directories( "../../lib/common/public" ) +include_directories( "../../lib/vc5_common" ) +include_directories( "../../lib/vc5_decoder" ) +include_directories( "../../lib/md5_lib" ) + +add_definitions("-DGPR_READING=1") + +# add executable +add_executable( ${EXE_NAME} ${DECODER_SRC_FILES} ${DECODER_INC_FILES} ${COMMON_SRC_FILES} ${COMMON_INC_FILES} ) + +target_link_libraries( ${EXE_NAME} vc5_decoder vc5_common common md5_lib argument_parser ) + +# set the folder where to place the projects +set_target_properties( ${EXE_NAME} PROPERTIES FOLDER app ) diff --git a/source/app/vc5_decoder_app/main.cpp b/source/app/vc5_decoder_app/main.cpp new file mode 100755 index 0000000..f9255ed --- /dev/null +++ b/source/app/vc5_decoder_app/main.cpp @@ -0,0 +1,223 @@ +/*! @file main.cpp + * + * @brief Main program file for the sample vc5 decoder. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdc_includes.h" +#include "stdcpp_includes.h" + +#if GPR_READING +#include "vc5_decoder.h" +#endif + +#include "argument_parser.h" +#include "timer.h" +#include "gpr_buffer.h" +#include "log.h" +#include "logcurve.h" +#include "common_app_def.h" + +#define DECODER_RUN_COUNT 1 + +using namespace std; + +class my_argument_parser : public argument_parser +{ +protected: + + bool help; + + bool verbose; + +public: + + string log_curve_file_path; + + string output_pixel_format; + + string input_file_path; + + string output_file_path; + +public: + + bool get_verbose() { return verbose; } + + bool get_help() { return help; } + + void set_options() + { + command_options.addOptions() + /* long and short name */ /* variable to update */ /* default value */ /* help text */ + ("help", help, false, "Prints this help text") + ("verbose", verbose, false, "Verbosity of the output") + + ("InputFilePath,i", input_file_path, string(""), "Input file path") + + ("OutputPixelFormat,x", output_pixel_format, string("rggb14"), "Output pixel format [rggb12, rggb12p, rggb14, gbrg12, gbrg12p]") + + ("OutputFilePath,o", output_file_path, string(""), "Output file path") + + ("PrintLogCurve,l", log_curve_file_path, string(""), "File for encoding log curve output"); + ; + } +}; + +/*! + @brief Main entry point for the reference decoder + + The program takes two arguments: the pathname to the file that contains a + sample to decode and the pathname to the output file for the decoded image. + + The input file should contain a single encoded sample without any header. + + The output file can be a DPX file, otherwise the decoded image is written to the output + file without a header. + + The image is decoded to the same dimensions as the encoded image and the decoded format + is the same format as the original source image input to the encoder. +*/ +int main(int argc, char *argv[]) +{ + my_argument_parser args; + + char line[MAX_STDOUT_LINE]; + sprintf( line, "VC5 Decoder Version %d.%d.%d [%s @ %s] ", VC5_VERSION_MAJOR, VC5_VERSION_MINOR, VC5_VERSION_REVISION, GIT_BRANCH, GIT_COMMIT_HASH ); + + if( args.parse(argc, argv, line, "[0000000000]" ) ) + return -1; + + int i; + + CODEC_ERROR error = CODEC_ERROR_OKAY; + + vc5_decoder_parameters vc5_decoder_params; + vc5_decoder_parameters_set_default(&vc5_decoder_params); + vc5_decoder_params.enabled_parts = VC5_ENABLED_PARTS; + vc5_decoder_params.mem_alloc = malloc; + vc5_decoder_params.mem_free = free; + + if( strcmp(args.output_pixel_format.c_str(), "rggb12") == 0 ) + { + vc5_decoder_params.pixel_format = VC5_DECODER_PIXEL_FORMAT_RGGB_12; + } + else if( strcmp(args.output_pixel_format.c_str(), "rggb14") == 0 ) + { + vc5_decoder_params.pixel_format = VC5_DECODER_PIXEL_FORMAT_RGGB_14; + } + else if( strcmp(args.output_pixel_format.c_str(), "gbrg12") == 0 ) + { + vc5_decoder_params.pixel_format = VC5_DECODER_PIXEL_FORMAT_GBRG_12; + } + else if( strcmp(args.output_pixel_format.c_str(), "gbrg14") == 0 ) + { + vc5_decoder_params.pixel_format = VC5_DECODER_PIXEL_FORMAT_GBRG_14; + } + else + { + LogPrint("Invalid output format: %s", args.output_pixel_format.c_str()); + return -1; + } + + LogInit(); + + gpr_buffer vc5_image = { NULL, 0 }; + + // Print the flags indicating which parts are enabled for this encoder + LogPrint("Vc5 Input image: %s", args.input_file_path.c_str() ); + LogPrint("Raw Output file: %s", args.output_file_path.c_str() ); + + if( read_from_file( &vc5_image, args.input_file_path.c_str(), vc5_decoder_params.mem_alloc, vc5_decoder_params.mem_free ) ) + { + LogPrint("Could not read input file: %s", args.input_file_path.c_str()); + exit(-1); + } + + TIMER timer; // Performance timer + InitTimer(&timer); + + for (i = 0; i < DECODER_RUN_COUNT; i++) + { // Decode all frames + + gpr_buffer raw_image = { NULL, 0 }; + gpr_rgb_buffer rgb_image = { NULL, 0, 0, 0 }; + + StartTimer(&timer); + + LogPrint("%d ", i); + + vc5_decoder_process( &vc5_decoder_params, &vc5_image, &raw_image, &rgb_image ); + + StopTimer(&timer); + + fflush(stdout); + + assert( raw_image.buffer && raw_image.size > 0 ); + + if( write_to_file( &raw_image, args.output_file_path.c_str() ) ) + { + LogPrint("Error writing bitstream to location %s", args.output_file_path.c_str() ); + return -1; + } + + if( raw_image.buffer ) + { + vc5_decoder_params.mem_free(raw_image.buffer); + } + + if( rgb_image.buffer ) + { + vc5_decoder_params.mem_free(rgb_image.buffer); + } + } + + LogPrint("Decoding %.3f secs per frame", TimeSecs(&timer) / DECODER_RUN_COUNT ); + + if( args.log_curve_file_path != "" ) + { + LogPrint("Printing log curve to %s", args.log_curve_file_path.c_str() ); + + ofstream file; + file.open ( args.log_curve_file_path.c_str() ); + + + for( int i = 0; i < LOG_CURVE_TABLE_LENGTH; i++ ) + { + file.fill( '0' ); + file.width( 4 ); + file << i; + + file << ": "; + + file.fill( '0' ); + file.width( 4 ); + file << (DecoderLogCurve[i] >> 4); + + file << endl; + } + + file.close(); + } + + if( vc5_image.buffer ) + { + vc5_decoder_params.mem_free( vc5_image.buffer ); + } + + LogUninit(); + + return error; +} diff --git a/source/app/vc5_encoder_app/CMakeLists.txt b/source/app/vc5_encoder_app/CMakeLists.txt new file mode 100644 index 0000000..8e24526 --- /dev/null +++ b/source/app/vc5_encoder_app/CMakeLists.txt @@ -0,0 +1,25 @@ +# executable +set( EXE_NAME vc5_encoder_app ) + +# get source and include files +file( GLOB ENCODER_SRC_FILES "*.c" "*.cpp" ) +file( GLOB ENCODER_INC_FILES "*.h" "../common/*.h" ) + +# add include files from other folders +include_directories( "../common" ) +include_directories( "../common/argument_parser" ) +include_directories( "../../lib/common/private" ) +include_directories( "../../lib/common/public" ) +include_directories( "../../lib/vc5_common" ) +include_directories( "../../lib/vc5_encoder" ) +include_directories( "../../lib/md5_lib" ) + +add_definitions("-DGPR_WRITING=1") + +# add executable +add_executable( ${EXE_NAME} ${ENCODER_SRC_FILES} ${ENCODER_INC_FILES} ${COMMON_SRC_FILES} ${COMMON_INC_FILES} ) + +target_link_libraries( ${EXE_NAME} vc5_encoder vc5_common common md5_lib argument_parser ) + +# set the folder where to place the projects +set_target_properties( ${EXE_NAME} PROPERTIES FOLDER app ) diff --git a/source/app/vc5_encoder_app/main.cpp b/source/app/vc5_encoder_app/main.cpp new file mode 100755 index 0000000..b82254d --- /dev/null +++ b/source/app/vc5_encoder_app/main.cpp @@ -0,0 +1,283 @@ +/*! @file main.cpp + * + * @brief Main program file for the sample vc5 encoder. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "stdc_includes.h" +#include "stdcpp_includes.h" + +#include "vc5_encoder.h" + +#include "argument_parser.h" +#include "timer.h" +#include "gpr_buffer.h" +#include "md5.h" +#include "log.h" +#include "logcurve.h" +#include "common_app_def.h" + +using namespace std; + +class my_argument_parser : public argument_parser +{ +protected: + + bool help; + + bool verbose; + +public: + + bool validate; + + bool dump_info; + + int input_width; + + int input_height; + + int input_pitch; + + int input_skip_rows; + + string log_curve_file_path; + + string input_pixel_format; + + string input_file_path; + + string output_file_path; + +public: + + bool get_verbose() { return verbose; } + + bool get_help() { return help; } + + void set_options() + { + command_options.addOptions() + /* long and short name */ /* variable to update */ /* default value */ /* help text */ + ("help", help, false, "Prints this help text") + ("verbose", verbose, false, "Verbosity of the output") + + ("InputFilePath,i", input_file_path, string(""), "Input file path") + + ("InputWidth,w", input_width, 4000, "Input image width in pixel samples e.g. 4000") + + ("InputHeight,h", input_height, 3000, "Input image height in pixel samples e.g. 3000") + + ("InputPitch,p", input_pitch, -1, "Input image pitch in bytes e.g. 8000") + + ("InputPixelFormat,x", input_pixel_format, string("rggb14"), "Input pixel format [rggb12, rggb12p, rggb14, gbrg12, gbrg12p]") + + ("OutputFilePath,o", output_file_path, string(""), "Output file path") + + ("PrintLogCurve,l", log_curve_file_path, string(""), "File for encoding log curve output"); + ; + } +}; + +/*! + @brief Main entry point for the reference encoder + + Usage: encoder [options] input output + + The input argument is the pathname to a file that contains a single image that + is the input the image unpacking process (see @ref ImageUnpackingProcess). The output argument is + the pathname to a file that will contain the encoded bitstream_file. Media containers are not + currently supported by the reference encoder. The command-line options are described in @ref ParseParameters. +*/ +int main(int argc, char *argv[]) +{ + my_argument_parser args; + + LogInit(); + + char line[MAX_STDOUT_LINE]; + sprintf( line, "VC5 Encoder Version %d.%d.%d [%s @ %s] ", VC5_VERSION_MAJOR, VC5_VERSION_MINOR, VC5_VERSION_REVISION, GIT_BRANCH, GIT_COMMIT_HASH ); + + if( args.parse(argc, argv, line, "[0000000000]") ) + return -1; + + int encoder_run; + int encoder_run_count = 1; + + CODEC_ERROR error = CODEC_ERROR_OKAY; + + vc5_encoder_parameters vc5_encoder_params; + vc5_encoder_parameters_set_default(&vc5_encoder_params); + + vc5_encoder_params.enabled_parts = VC5_ENABLED_PARTS; + vc5_encoder_params.input_width = args.input_width; + vc5_encoder_params.input_height = args.input_height; + vc5_encoder_params.input_pitch = args.input_pitch; + vc5_encoder_params.mem_alloc = malloc; + vc5_encoder_params.mem_free = free; + + if( strcmp(args.input_pixel_format.c_str(), "rggb12") == 0 ) + { + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_RGGB_12; + + if( args.input_pitch == -1 ) + vc5_encoder_params.input_pitch = args.input_width * 2; + } + if( strcmp(args.input_pixel_format.c_str(), "rggb12p") == 0 ) + { + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_RGGB_12P; + + if( args.input_pitch == -1 ) + vc5_encoder_params.input_pitch = (args.input_width * 3 / 4) * 2; + } + else if( strcmp(args.input_pixel_format.c_str(), "rggb14") == 0 ) + { + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_RGGB_14; + + if( args.input_pitch == -1 ) + args.input_pitch = args.input_width * 2; + } + else if( strcmp(args.input_pixel_format.c_str(), "gbrg12") == 0 ) + { + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_GBRG_12; + + if( args.input_pitch == -1 ) + args.input_pitch = args.input_width * 2; + } + else if( strcmp(args.input_pixel_format.c_str(), "gbrg12p") == 0 ) + { + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_GBRG_12P; + + if( args.input_pitch == -1 ) + args.input_pitch = (args.input_width * 3 / 4) * 2; + } + else + { + LogPrint("Invalid input format: %s", args.input_pixel_format.c_str()); + return -1; + } + + gpr_buffer raw_image = { NULL, 0 }; + + // Print the flags indicating which parts are enabled for this encoder + LogPrint("Raw Input image: %s", args.input_file_path.c_str() ); + LogPrint("Vc5 Output file: %s", args.output_file_path.c_str() ); + + if( read_from_file( &raw_image, args.input_file_path.c_str(), vc5_encoder_params.mem_alloc, vc5_encoder_params.mem_free ) ) + { + LogPrint("Could not read input file: %s", args.input_file_path.c_str()); + return -1; + } + + TIMER timer; + InitTimer(&timer); + + unsigned char old_digest[MD5_DIGEST_SIZE]; + + for (encoder_run = 0; encoder_run < encoder_run_count; encoder_run++) + { + gpr_buffer vc5_image = { NULL, 0 }; + + StartTimer(&timer); + + vc5_encoder_process( &vc5_encoder_params, &raw_image, &vc5_image, NULL ); + + StopTimer(&timer); + + assert( vc5_image.buffer && vc5_image.size > 0 ); + + if( write_to_file( &vc5_image, args.output_file_path.c_str() ) ) + { + LogPrint("Error writing bitstream to location %s", args.output_file_path.c_str() ); + return -1; + } + + { + static const char* hex = "0123456789ABCDEF"; + + unsigned char digest[MD5_DIGEST_SIZE]; + context_md5_t ctx; + + MD5Init(&ctx); + MD5Update(&ctx, (unsigned char *)vc5_image.buffer, vc5_image.size ); + MD5Final(digest, &ctx); + + { + int i; + unsigned char logged_digest[MD5_DIGEST_SIZE * 2 + 1]; + + for (i = 0; i < MD5_DIGEST_SIZE; i++) + { + logged_digest[i * 2 + 0] = hex[ digest[i] >> 4 ]; + logged_digest[i * 2 + 1] = hex[ digest[i] & 0xf ]; + } + + logged_digest[i * 2] = '\0'; + + LogPrint("%d %s", encoder_run, logged_digest); + } + + if( encoder_run > 0 ) + { + if( memcmp(old_digest, digest, sizeof(digest) ) ) + { + LogPrint("ERROR digests in run %d and %d do not match", encoder_run, encoder_run - 1 ); + return -1; + } + } + + memcpy(old_digest, digest, sizeof(digest)); + } + + vc5_encoder_params.mem_free(vc5_image.buffer); + } + + LogPrint("Encoding %.3f secs per frame", TimeSecs(&timer) / encoder_run_count ); + + if( args.log_curve_file_path != "" ) + { + LogPrint("Printing log curve to %s", args.log_curve_file_path.c_str() ); + + ofstream file; + file.open ( args.log_curve_file_path.c_str() ); + + + for( int i = 0; i < LOG_CURVE_TABLE_LENGTH; i++ ) + { + file.fill( '0' ); + file.width( 4 ); + file << i; + + file << ": "; + + file.fill( '0' ); + file.width( 4 ); + file << EncoderLogCurve[i]; + + file << endl; + } + + file.close(); + } + + if( raw_image.buffer ) + { + vc5_encoder_params.mem_free( raw_image.buffer ); + } + + LogUninit(); + + return error; +} diff --git a/source/lib/common/CMakeLists.txt b/source/lib/common/CMakeLists.txt new file mode 100644 index 0000000..103b6c2 --- /dev/null +++ b/source/lib/common/CMakeLists.txt @@ -0,0 +1,18 @@ +# library +set( LIB_NAME common ) + +# get source files +file( GLOB SRC_FILES "private/*.cpp" "private/*.c" ) + +# get include files +file( GLOB INC_FILES "private/*.h" "public/*.h" ) + +include_directories( "./public" ) + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/source/lib/common/private/gpr_allocator.c b/source/lib/common/private/gpr_allocator.c new file mode 100644 index 0000000..366bdf8 --- /dev/null +++ b/source/lib/common/private/gpr_allocator.c @@ -0,0 +1,44 @@ +/*! @file gpr_allocator.c + * + * @brief Implementation of the global memory allocation functions. + * In order to customize memory allocation and deallocation, applications + * can implement these functions in additional files and include in + * build process + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gpr_allocator.h" + +/*! + @brief Allocate a block with the specified size + */ +void* gpr_global_malloc(size_t size) +{ + return malloc(size); +} + +/*! + @brief Free a block that was allocated by the specified allocator + + It is an error to free a block allocated by one allocator using a + different allocator. + */ +void gpr_global_free(void *block) +{ + free(block); +} + diff --git a/source/lib/common/private/gpr_buffer.c b/source/lib/common/private/gpr_buffer.c new file mode 100755 index 0000000..71aa878 --- /dev/null +++ b/source/lib/common/private/gpr_buffer.c @@ -0,0 +1,97 @@ +/*! @file gpr_buffer.c + * + * @brief Implementation of gpr_buffer object and functions that work on buffer + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gpr_buffer.h" +#include "macros.h" +#include "stdc_includes.h" + +int read_from_file(gpr_buffer* buffer, const char* file_path, gpr_malloc malloc_function, gpr_free free_function) +{ + assert( buffer != NULL ); + + FILE *fIN = fopen(file_path, "rb"); + if (fIN == NULL) + { + fprintf (stderr, "Error while reading file: %s", file_path); + return -1; + } + + fseek (fIN, 0, SEEK_END); + buffer->size = (int32_t) ftell(fIN); + rewind (fIN); + + buffer->buffer = malloc_function(buffer->size); + + if ( buffer->buffer == NULL) + { + fputs ("Memory error", stderr); + return -1; + } + + long result = fread(buffer->buffer, 1, buffer->size, fIN); + if (result != buffer->size) + { + free_function(buffer->buffer); + fputs ("Reading error", stderr); + return -1; + } + + fclose(fIN); + + return 0; +} + +int write_to_file(const gpr_buffer* buffer, const char* file_path) +{ + unsigned int bytes_written; + + FILE *fOUT = fopen(file_path, "wb"); + if (fOUT == NULL) + { + fprintf (stderr, "Error while writing file: %s", file_path); + return -1; + } + + bytes_written = fwrite(buffer->buffer, 1, buffer->size, fOUT); + if( bytes_written != buffer->size ) { + fputs("Could not write bytes \n", stderr); + perror("fwrite()"); + return -2; + } + + fclose(fOUT); + + return 0; +} + +#include "gpr_rgb_buffer.h" + +void gpr_rgb_gain_set_defaults(gpr_rgb_gain* x) +{ + x->r_gain_num = 30; + x->r_gain_pow2_den = 4; + + x->g_gain_num = 1; + x->g_gain_pow2_den = 0; + + x->b_gain_num = 7; + x->b_gain_pow2_den = 2; +} + diff --git a/source/lib/common/private/gpr_buffer_auto.cpp b/source/lib/common/private/gpr_buffer_auto.cpp new file mode 100755 index 0000000..736c139 --- /dev/null +++ b/source/lib/common/private/gpr_buffer_auto.cpp @@ -0,0 +1,45 @@ +/*! @file gpr_buffer_auto.cpp + * + * @brief Implementation of gpr_buffer_auto object. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gpr_buffer.h" +#include "gpr_buffer_auto.h" + +#include + +int gpr_buffer_auto::read_from_file(const char* file_path) +{ + assert( is_valid() == false ); + + int return_code = ::read_from_file(&buffer, file_path, mem_alloc, mem_free ); + + if( return_code == 0 ) + { + free_in_destructor = true; + } + + return return_code; +} + +int gpr_buffer_auto::write_to_file(const char* file_path) +{ + assert( is_valid() ); + + return ::write_to_file(&buffer, file_path); +} diff --git a/source/lib/common/private/gpr_buffer_auto.h b/source/lib/common/private/gpr_buffer_auto.h new file mode 100755 index 0000000..e4a2eee --- /dev/null +++ b/source/lib/common/private/gpr_buffer_auto.h @@ -0,0 +1,148 @@ +/*! @file gpr_internal_buffer + * + * @brief Declaration of gpr_buffer_auto object. This object + * implements a buffer that carries size information. gpr_buffer_auto can + * deallocate itself in destructor (depending on free_in_destructor member) + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_INTERNAL_BUFFER_H +#define GPR_INTERNAL_BUFFER_H + +#include "gpr_allocator.h" +#include "gpr_buffer.h" +#include "gpr_platform.h" +#include "stdc_includes.h" + +class gpr_buffer_auto +{ +private: + gpr_buffer buffer; + + bool free_in_destructor; + + gpr_malloc mem_alloc; + + gpr_free mem_free; + +public: + void reset() + { + buffer.buffer = NULL; + buffer.size = 0; + free_in_destructor = false; + } + + gpr_buffer_auto(gpr_malloc malloc_function, gpr_free free_function) + { + reset(); + + mem_alloc = malloc_function; + mem_free = free_function; + } + + ~gpr_buffer_auto() + { + deallocate(); + } + + void allocate(size_t size_of_memory) + { + assert(buffer.buffer == NULL); + assert(buffer.size == 0); + + buffer.size = size_of_memory; + + if(buffer.size > 0) + { + buffer.buffer = mem_alloc(buffer.size); + assert(buffer.buffer); + + free_in_destructor = true; + } + } + + void deallocate() + { + if(buffer.buffer && free_in_destructor) + mem_free( (char*)buffer.buffer ); + + reset(); + } + + void resize( size_t new_size ) + { + buffer.size = new_size; + + assert(buffer.buffer); + assert(buffer.size); + } + + void set(void* _buffer, size_t _size, bool _free_in_destructor = false ) + { + assert(buffer.buffer == NULL); + assert(buffer.size == 0); + + buffer.buffer = _buffer; + buffer.size = _size; + free_in_destructor = _free_in_destructor; + + assert(buffer.buffer); + assert(buffer.size); + } + + void zero() + { + // Cant call zero for the case when buffer has been preallocated + // Call deallocate() + assert( free_in_destructor == false ); + + buffer.buffer = NULL; + buffer.size = 0; + } + + bool is_valid() const + { + return ( buffer.buffer != NULL ) && buffer.size > 0; + } + +// Accessors + void* get_buffer() const { return buffer.buffer; } + + char* to_char() const { return (char*)buffer.buffer; } + + unsigned char* to_uchar() const { return (unsigned char*)buffer.buffer; } + + unsigned short* to_ushort() const { return (unsigned short*)buffer.buffer; } + + uint16_t* to_uint16_t() const { return (uint16_t*)buffer.buffer; } + + size_t get_size() const { return buffer.size; } + + gpr_malloc get_malloc() const { return mem_alloc; } + + gpr_free get_free() const { return mem_free; } + +// Operations + int read_from_file(const char* file_path); + + int write_to_file(const char* file_path); + + const gpr_buffer& get_gpr_buffer() { return buffer; } +}; + +#endif // GPR_INTERNAL_BUFFER_H diff --git a/source/lib/common/private/log.c b/source/lib/common/private/log.c new file mode 100755 index 0000000..99dcd18 --- /dev/null +++ b/source/lib/common/private/log.c @@ -0,0 +1,60 @@ +/*! @file gpr_log.c + * + * @brief Implementation of functions used for logging + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "timer.h" +#include "stdc_includes.h" + +TIMER LogTimer; + +bool LogInit() +{ + InitTimer(&LogTimer); + + return true; +} + +#ifndef LogPrint +int LogPrint(const char* format, ... ) +{ + StopTimer(&LogTimer); + + printf("[%5d-ms] ", (unsigned int)TimeMSecs(&LogTimer)); + + { + va_list argptr; + va_start(argptr, format); + + vfprintf(stdout, format, argptr); + + va_end(argptr); + } + + printf( "%c", '\n' ); + + StartTimer(&LogTimer); + + return 0; +} +#endif // LogPrint + +bool LogUninit() +{ + return true; +} diff --git a/source/lib/common/private/log.h b/source/lib/common/private/log.h new file mode 100755 index 0000000..51c882d --- /dev/null +++ b/source/lib/common/private/log.h @@ -0,0 +1,60 @@ +/*! @file gpr_log.h + * + * @brief Declatation of functions used for logging + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_LOG_H +#define GPR_LOG_H + +#ifdef __cplusplus +extern "C" { +#endif + + bool LogInit(); + + #ifndef LogPrint + int LogPrint(const char* format, ... ); + #endif + + bool LogUninit(); + + #define TIMESTAMP(x, y) TIMESTAMP_##y(x) + + #if GPR_TIMING == 0 + #define TIMESTAMP_3(x) + #define TIMESTAMP_2(x) + #define TIMESTAMP_1(x) + #elif GPR_TIMING == 1 + #define TIMESTAMP_3(x) + #define TIMESTAMP_2(x) + #define TIMESTAMP_1(x) LogPrint("%s %s() %s (line %d)", x, __FUNCTION__, __FILE__, __LINE__); + #elif GPR_TIMING == 2 + #define TIMESTAMP_3(x) + #define TIMESTAMP_2(x) LogPrint("%s %s() %s (line %d)", x, __FUNCTION__, __FILE__, __LINE__); + #define TIMESTAMP_1(x) LogPrint("%s %s() %s (line %d)", x, __FUNCTION__, __FILE__, __LINE__); + #elif GPR_TIMING == 3 + #define TIMESTAMP_3(x) LogPrint("%s %s() %s (line %d)", x, __FUNCTION__, __FILE__, __LINE__); + #define TIMESTAMP_2(x) LogPrint("%s %s() %s (line %d)", x, __FUNCTION__, __FILE__, __LINE__); + #define TIMESTAMP_1(x) LogPrint("%s %s() %s (line %d)", x, __FUNCTION__, __FILE__, __LINE__); + #endif + +#ifdef __cplusplus +} +#endif + +#endif // GPR_LOG_H diff --git a/source/lib/common/private/macros.h b/source/lib/common/private/macros.h new file mode 100755 index 0000000..5f3e9b5 --- /dev/null +++ b/source/lib/common/private/macros.h @@ -0,0 +1,84 @@ +/*! @file gpr_macros.h + * + * @brief Definitions of useful inline functions that are used everywhere in code. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_MACROS_H +#define GPR_MACROS_H + +#ifndef neg + #define neg(x) (-(x)) +#endif + +#define DivideByShift(x, s) ((x) >> (s)) + +STATIC_INLINE uint16_t clamp_uint(int32_t value, uint32_t precision) +{ + const int32_t limit = ((1 << precision) - 1); + + if (value < 0) + value = 0; + else if (value > limit) + value = limit; + + return (uint16_t)value; +} + +STATIC_INLINE uint16_t clamp_uint16(int32_t value) +{ + return (uint16_t)clamp_uint( value, 16); +} + +STATIC_INLINE uint16_t clamp_uint14(int32_t value) +{ + return (uint16_t)clamp_uint( value, 14); +} + +STATIC_INLINE uint16_t clamp_uint12(int32_t value) +{ + return (uint16_t)clamp_uint( value, 12); +} + +STATIC_INLINE uint8_t clamp_uint8(int32_t value) +{ + return (uint8_t)clamp_uint( value, 8); +} + +STATIC_INLINE int minimum(int a, int b) +{ + return (a < b) ? a : b; +} + +STATIC_INLINE int maximum(int a, int b) +{ + return (a < b) ? b : a; +} + +STATIC_INLINE int absolute(int a) +{ + return (a < 0) ? -a : a; +} + +STATIC_INLINE uint32_t Swap32(uint32_t value) +{ + value = (value & 0x0000FFFF) << 16 | (value & 0xFFFF0000) >> 16; + value = (value & 0x00FF00FF) << 8 | (value & 0xFF00FF00) >> 8; + return value; +} + +#endif // GPR_MACROS_H diff --git a/source/lib/common/private/stdc_includes.h b/source/lib/common/private/stdc_includes.h new file mode 100755 index 0000000..dff18de --- /dev/null +++ b/source/lib/common/private/stdc_includes.h @@ -0,0 +1,33 @@ +/*! @file stdc_includes.h + * + * @brief Standard C include files used by package + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include \ No newline at end of file diff --git a/source/lib/common/private/stdcpp_includes.h b/source/lib/common/private/stdcpp_includes.h new file mode 100755 index 0000000..3da3a27 --- /dev/null +++ b/source/lib/common/private/stdcpp_includes.h @@ -0,0 +1,22 @@ +/*! @file stdcpp_includes.h + * + * @brief Standard C++ include files used by package + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include + diff --git a/source/lib/common/private/timer.c b/source/lib/common/private/timer.c new file mode 100755 index 0000000..4db60e7 --- /dev/null +++ b/source/lib/common/private/timer.c @@ -0,0 +1,54 @@ +/*! @file gpr_timer.c + * + * @brief Implementation of a high-resolution performance timer. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "timer.h" + +/*! + @brief Initialize a timer + + The frequency of the performance timer is determined if it has not + already been obtained. + */ +void InitTimer(TIMER *timer) +{ + timer->begin = 0; + timer->elapsed = 0; +} + +void StartTimer(TIMER *timer) +{ + timer->begin = clock(); +} + +void StopTimer(TIMER *timer) +{ + timer->elapsed += (clock() - timer->begin); +} + +float TimeSecs(TIMER *timer) +{ + return (float)(timer->elapsed) / CLOCKS_PER_SEC; +} + +float TimeMSecs(TIMER *timer) +{ + return (float)(TimeSecs(timer) * 1000); +} + diff --git a/source/lib/common/private/timer.h b/source/lib/common/private/timer.h new file mode 100755 index 0000000..9e05869 --- /dev/null +++ b/source/lib/common/private/timer.h @@ -0,0 +1,51 @@ +/*! @file gpr_timer.h + * + * @brief Declaration of a high-resolution performance timer. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_TIMER_H +#define GPR_TIMER_H + +#include + +typedef struct timer +{ + clock_t begin; + clock_t elapsed; + +} TIMER; + +#ifdef __cplusplus +extern "C" { +#endif + + void InitTimer(TIMER *timer); + + void StartTimer(TIMER *timer); + + void StopTimer(TIMER *timer); + + float TimeSecs(TIMER *timer); + + float TimeMSecs(TIMER *timer); + +#ifdef __cplusplus +} +#endif + +#endif // GPR_TIMER_H diff --git a/source/lib/common/public/gpr_allocator.h b/source/lib/common/public/gpr_allocator.h new file mode 100755 index 0000000..0f002a0 --- /dev/null +++ b/source/lib/common/public/gpr_allocator.h @@ -0,0 +1,50 @@ +/*! @file gpr_allocator.h + * + * @brief The API for memory allocation and deallocation functions. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_ALLOCATOR_H +#define GPR_ALLOCATOR_H + +#include "gpr_platform.h" + +#ifdef __cplusplus +extern "C" { +#endif + + extern void* gpr_global_malloc(size_t size); + + extern void gpr_global_free(void *block); + + typedef void* (*gpr_malloc)(size_t size); /* Malloc callback function typedef */ + + typedef void (*gpr_free)(void* p); /* Free callback function typedef */ + + typedef struct + { + gpr_malloc Alloc; // Callback function to allocate memory + + gpr_free Free; // Callback function to free memory + + } gpr_allocator; + +#ifdef __cplusplus +} +#endif + +#endif // GPR_ALLOCATOR_H diff --git a/source/lib/common/public/gpr_buffer.h b/source/lib/common/public/gpr_buffer.h new file mode 100755 index 0000000..beefb23 --- /dev/null +++ b/source/lib/common/public/gpr_buffer.h @@ -0,0 +1,47 @@ +/*! @file gpr_buffer.h + * + * @brief Declaration of gpr_buffer object and functions that work on buffer + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_BUFFER_H +#define GPR_BUFFER_H + +#include "gpr_allocator.h" +#include "gpr_platform.h" + +#ifdef __cplusplus + extern "C" { +#endif + + typedef struct + { + void* buffer; /* Address to the memory location that this buffer points to */ + + size_t size; /* Size of the memory block */ + + } gpr_buffer; + + int read_from_file(gpr_buffer* buffer, const char* file_path, gpr_malloc malloc_function, gpr_free free_function); + + int write_to_file(const gpr_buffer* buffer, const char* file_path); + +#ifdef __cplusplus + } +#endif + +#endif // GPR_BUFFER_H diff --git a/source/lib/common/public/gpr_platform.h b/source/lib/common/public/gpr_platform.h new file mode 100755 index 0000000..14ffcc5 --- /dev/null +++ b/source/lib/common/public/gpr_platform.h @@ -0,0 +1,162 @@ +/*! @file gpr_platform.h + * + * @brief Definitions of platform related settings + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_PLATFORM_H +#define GPR_PLATFORM_H + +// ================================================================================================= +// Timing output. Define to: +// 0 for applications (disabled all timing code) +// 1 to enable top level timing +// 2 to enable low level timing +// ================================================================================================= + +#ifndef GPR_TIMING + #define GPR_TIMING 1 +#endif + +// ================================================================================================= +// Defines to enable encoder and decoder API in GPR-SDK +// ================================================================================================= + +#ifndef GPR_WRITING + #define GPR_WRITING 1 +#endif + +#ifndef GPR_READING + #define GPR_READING 1 +#endif + +#ifndef GPR_JPEG_AVAILABLE + #define GPR_JPEG_AVAILABLE 1 +#endif + +// ================================================================================================= +// Flags to enable/disable SIMD code +// ================================================================================================= + +// Neon code is implemented for encoder +#ifndef NEON + #define NEON 0 +#endif + +// SSE code is not implemented yet +#ifndef SSE + #define SSE 0 +#endif + +// ================================================================================================= +// Do not change lines below +// ================================================================================================= + +// ================================================================================================= +// Common header files needed by GPR-SDK +// ================================================================================================= +#include +#include + +#ifndef float_t +typedef float float_t; +#endif + +#ifdef _WIN32 + +// Turn off warnings about deprecated functions +#pragma warning(disable: 4996) + +#define INLINE __inline + +#else + +#define INLINE inline + +#endif // _WIN32 + +#define STATIC static + +#define STATIC_INLINE STATIC INLINE + +#define ENABLED(x) (x) + +// ================================================================================================= +// Determine the Platform +// ================================================================================================= + +#ifdef _WIN32 + + #define qWinOS 1 + #define qMacOS 0 + #define qLinux 0 + #define qiPhone 0 + #define qAndroid 0 + +#elif __APPLE__ + + #include "TargetConditionals.h" + + #define qEnableCarbon 0 // Disable Carbon API because it is old and deprecated by Apple + + #if TARGET_OS_IPHONE + + #define qWinOS 0 + #define qMacOS 0 + #define qLinux 0 + #define qiPhone 1 + #define qAndroid 0 + + #else + + #define qWinOS 0 + #define qMacOS 1 + #define qLinux 0 + #define qiPhone 0 + #define qAndroid 0 + + #endif + +#elif __ANDROID__ + + #define qWinOS 0 + #define qMacOS 0 + #define qLinux 0 + #define qiPhone 0 + #define qAndroid 1 + +#elif __linux__ || __unix__ + + #define qWinOS 0 + #define qMacOS 0 + #define qLinux 1 + #define qiPhone 0 + #define qAndroid 0 + +#else + #error "XMP environment error - Unknown compiler" +#endif + +// ================================================================================================= +// GPR version numbering +// ================================================================================================= + +#define GPR_VERSION_MAJOR 1 +#define GPR_VERSION_MINOR 0 +#define GPR_VERSION_REVISION 0 + +#endif // GPR_PLATFORM_H diff --git a/source/lib/common/public/gpr_rgb_buffer.h b/source/lib/common/public/gpr_rgb_buffer.h new file mode 100755 index 0000000..6fcbb8d --- /dev/null +++ b/source/lib/common/public/gpr_rgb_buffer.h @@ -0,0 +1,77 @@ +/*! @file gpr_buffer.h + * + * @brief Declaration of gpr_rgb_buffer object and enums/functions that work on it + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_RGB_BUFFER_H +#define GPR_RGB_BUFFER_H + +#include "gpr_allocator.h" +#include "gpr_platform.h" + +#ifdef __cplusplus + extern "C" { +#endif + + typedef enum + { + GPR_RGB_RESOLUTION_SIXTEENTH = 1, + GPR_RGB_RESOLUTION_EIGHTH = 2, + GPR_RGB_RESOLUTION_QUARTER = 3, + GPR_RGB_RESOLUTION_HALF = 4, + GPR_RGB_RESOLUTION_FULL = 5, + + GPR_RGB_RESOLUTION_NONE = 6, + + GPR_RGB_RESOLUTION_DEFAULT = GPR_RGB_RESOLUTION_QUARTER, + + } GPR_RGB_RESOLUTION; + + + typedef struct + { + int r_gain_num; + int r_gain_pow2_den; + + int g_gain_num; + int g_gain_pow2_den; + + int b_gain_num; + int b_gain_pow2_den; + + } gpr_rgb_gain; + + void gpr_rgb_gain_set_defaults(gpr_rgb_gain* x); + + typedef struct + { + void* buffer; /* Address to the memory location that this buffer points to */ + + size_t size; /* Size of the memory block */ + + size_t width; + + size_t height; + + } gpr_rgb_buffer; + +#ifdef __cplusplus + } +#endif + +#endif // GPR_RGB_BUFFER_H diff --git a/source/lib/dng_sdk/BSD-License.txt b/source/lib/dng_sdk/BSD-License.txt new file mode 100644 index 0000000..07b967c --- /dev/null +++ b/source/lib/dng_sdk/BSD-License.txt @@ -0,0 +1,32 @@ +The BSD License + +Copyright (c) 1999 - 2014, Adobe Systems Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems Incorporated, nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/source/lib/dng_sdk/CMakeLists.txt b/source/lib/dng_sdk/CMakeLists.txt new file mode 100644 index 0000000..40f9a92 --- /dev/null +++ b/source/lib/dng_sdk/CMakeLists.txt @@ -0,0 +1,21 @@ +# library +set( LIB_NAME dng_sdk ) + +# get source files +file( GLOB SRC_FILES "*.cpp" ) + +# get include files +file( GLOB INC_FILES "*.h" ) + +# add include files from other folders +include_directories( "../common/private" ) +include_directories( "../common/public" ) +include_directories( "../xmp_core/public/include" ) + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/source/lib/dng_sdk/RawEnvironment.h b/source/lib/dng_sdk/RawEnvironment.h new file mode 100644 index 0000000..7b9a3f3 --- /dev/null +++ b/source/lib/dng_sdk/RawEnvironment.h @@ -0,0 +1,12 @@ +/* + * + * ARM Cortex Environment + * + * + */ + +#ifndef __RawEnvironment_ + +#define qDNGLittleEndian 1 + +#endif // __RawEnvironment diff --git a/source/lib/dng_sdk/dng_1d_function.cpp b/source/lib/dng_sdk/dng_1d_function.cpp new file mode 100644 index 0000000..3ff79f4 --- /dev/null +++ b/source/lib/dng_sdk/dng_1d_function.cpp @@ -0,0 +1,195 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_1d_function.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_1d_function.h" + +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_1d_function::~dng_1d_function () + { + + } + +/*****************************************************************************/ + +bool dng_1d_function::IsIdentity () const + { + + return false; + + } + +/*****************************************************************************/ + +real64 dng_1d_function::EvaluateInverse (real64 y) const + { + + const uint32 kMaxIterations = 30; + const real64 kNearZero = 1.0e-10; + + real64 x0 = 0.0; + real64 y0 = Evaluate (x0); + + real64 x1 = 1.0; + real64 y1 = Evaluate (x1); + + for (uint32 iteration = 0; iteration < kMaxIterations; iteration++) + { + + if (Abs_real64 (y1 - y0) < kNearZero) + { + break; + } + + real64 x2 = Pin_real64 (0.0, + x1 + (y - y1) * (x1 - x0) / (y1 - y0), + 1.0); + + real64 y2 = Evaluate (x2); + + x0 = x1; + y0 = y1; + + x1 = x2; + y1 = y2; + + } + + return x1; + + } + +/*****************************************************************************/ + +bool dng_1d_identity::IsIdentity () const + { + + return true; + + } + +/*****************************************************************************/ + +real64 dng_1d_identity::Evaluate (real64 x) const + { + + return x; + + } + +/*****************************************************************************/ + +real64 dng_1d_identity::EvaluateInverse (real64 x) const + { + + return x; + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_1d_identity::Get () + { + + static dng_1d_identity static_function; + + return static_function; + + } + +/*****************************************************************************/ + +dng_1d_concatenate::dng_1d_concatenate (const dng_1d_function &function1, + const dng_1d_function &function2) + + : fFunction1 (function1) + , fFunction2 (function2) + + { + + } + +/*****************************************************************************/ + +bool dng_1d_concatenate::IsIdentity () const + { + + return fFunction1.IsIdentity () && + fFunction2.IsIdentity (); + + } + +/*****************************************************************************/ + +real64 dng_1d_concatenate::Evaluate (real64 x) const + { + + real64 y = Pin_real64 (0.0, fFunction1.Evaluate (x), 1.0); + + return fFunction2.Evaluate (y); + + } + +/*****************************************************************************/ + +real64 dng_1d_concatenate::EvaluateInverse (real64 x) const + { + + real64 y = fFunction2.EvaluateInverse (x); + + return fFunction1.EvaluateInverse (y); + + } + +/*****************************************************************************/ + +dng_1d_inverse::dng_1d_inverse (const dng_1d_function &f) + + : fFunction (f) + + { + + } + +/*****************************************************************************/ + +bool dng_1d_inverse::IsIdentity () const + { + + return fFunction.IsIdentity (); + + } + +/*****************************************************************************/ + +real64 dng_1d_inverse::Evaluate (real64 x) const + { + + return fFunction.EvaluateInverse (x); + + } + +/*****************************************************************************/ + +real64 dng_1d_inverse::EvaluateInverse (real64 y) const + { + + return fFunction.Evaluate (y); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_1d_function.h b/source/lib/dng_sdk/dng_1d_function.h new file mode 100644 index 0000000..aee59ca --- /dev/null +++ b/source/lib/dng_sdk/dng_1d_function.h @@ -0,0 +1,159 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_1d_function.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Classes for a 1D floating-point to floating-point function abstraction. + */ + +/*****************************************************************************/ + +#ifndef __dng_1d_function__ +#define __dng_1d_function__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief A 1D floating-point function. +/// +/// The domain (input) is always from 0.0 to 1.0, while the range (output) can be an arbitrary interval. + +class dng_1d_function + { + + public: + + virtual ~dng_1d_function (); + + /// Returns true if this function is the map x -> y such that x == y for all x . That is if Evaluate(x) == x for all x. + + virtual bool IsIdentity () const; + + /// Return the mapping for value x. + /// This method must be implemented by a derived class of dng_1d_function and the derived class determines the + /// lookup method and function used. + /// \param x A value between 0.0 and 1.0 (inclusive). + /// \retval Mapped value for x + + virtual real64 Evaluate (real64 x) const = 0; + + /// Return the reverse mapped value for y. + /// This method can be implemented by derived classes. The default implementation uses Newton's method to solve + /// for x such that Evaluate(x) == y. + /// \param y A value to reverse map. Should be within the range of the function implemented by this dng_1d_function . + /// \retval A value x such that Evaluate(x) == y (to very close approximation). + + virtual real64 EvaluateInverse (real64 y) const; + + }; + +/*****************************************************************************/ + +/// An identity (x -> y such that x == y for all x) mapping function. + +class dng_1d_identity: public dng_1d_function + { + + public: + /// Always returns true for this class. + + virtual bool IsIdentity () const; + + /// Always returns x for this class. + + virtual real64 Evaluate (real64 x) const; + + /// Always returns y for this class. + + virtual real64 EvaluateInverse (real64 y) const; + + /// This class is a singleton, and is entirely threadsafe. Use this method to get an instance of the class. + + static const dng_1d_function & Get (); + + }; + +/*****************************************************************************/ + +/// A dng_1d_function that represents the composition (curry) of two other dng_1d_functions. + +class dng_1d_concatenate: public dng_1d_function + { + + protected: + + const dng_1d_function &fFunction1; + + const dng_1d_function &fFunction2; + + public: + + /// Create a dng_1d_function which computes y = function2.Evaluate(function1.Evaluate(x)). + /// Compose function1 and function2 to compute y = function2.Evaluate(function1.Evaluate(x)). The range of function1.Evaluate must be a subset of 0.0 to 1.0 inclusive, + /// otherwise the result of function1(x) will be pinned (clipped) to 0.0 if <0.0 and to 1.0 if > 1.0 . + /// \param function1 Inner function of composition. + /// \param function2 Outer function of composition. + + dng_1d_concatenate (const dng_1d_function &function1, + const dng_1d_function &function2); + + /// Only true if both function1 and function2 have IsIdentity equal to true. + + virtual bool IsIdentity () const; + + /// Return the composed mapping for value x. + /// \param x A value between 0.0 and 1.0 (inclusive). + /// \retval function2.Evaluate(function1.Evaluate(x)). + + virtual real64 Evaluate (real64 x) const; + + /// Return the reverse mapped value for y. + /// Be careful using this method with compositions where the inner function does not have a range 0.0 to 1.0 . (Or better yet, do not use such functions.) + /// \param y A value to reverse map. Should be within the range of function2.Evaluate. + /// \retval A value x such that function2.Evaluate(function1.Evaluate(x)) == y (to very close approximation). + + virtual real64 EvaluateInverse (real64 y) const; + + }; + +/*****************************************************************************/ + +/// A dng_1d_function that represents the inverse of another dng_1d_function. + +class dng_1d_inverse: public dng_1d_function + { + + protected: + + const dng_1d_function &fFunction; + + public: + + dng_1d_inverse (const dng_1d_function &f); + + virtual bool IsIdentity () const; + + virtual real64 Evaluate (real64 x) const; + + virtual real64 EvaluateInverse (real64 y) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_1d_table.cpp b/source/lib/dng_sdk/dng_1d_table.cpp new file mode 100644 index 0000000..267078e --- /dev/null +++ b/source/lib/dng_sdk/dng_1d_table.cpp @@ -0,0 +1,195 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_1d_table.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_1d_table.h" + +#include "dng_1d_function.h" +#include "dng_memory.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_1d_table::dng_1d_table () + + : fBuffer () + , fTable (NULL) + + { + + } + +/*****************************************************************************/ + +dng_1d_table::~dng_1d_table () + { + + } + +/*****************************************************************************/ + +void dng_1d_table::SubDivide (const dng_1d_function &function, + uint32 lower, + uint32 upper, + real32 maxDelta) + { + + uint32 range = upper - lower; + + bool subDivide = (range > (kTableSize >> 8)); + + if (!subDivide) + { + + real32 delta = Abs_real32 (fTable [upper] - + fTable [lower]); + + if (delta > maxDelta) + { + + subDivide = true; + + } + + } + + if (subDivide) + { + + uint32 middle = (lower + upper) >> 1; + + fTable [middle] = (real32) function.Evaluate (middle * (1.0 / (real64) kTableSize)); + + if (range > 2) + { + + SubDivide (function, lower, middle, maxDelta); + + SubDivide (function, middle, upper, maxDelta); + + } + + } + + else + { + + real64 y0 = fTable [lower]; + real64 y1 = fTable [upper]; + + real64 delta = (y1 - y0) / (real64) range; + + for (uint32 j = lower + 1; j < upper; j++) + { + + y0 += delta; + + fTable [j] = (real32) y0; + + } + + } + + } + +/*****************************************************************************/ + +void dng_1d_table::Initialize (dng_memory_allocator &allocator, + const dng_1d_function &function, + bool subSample) + { + + fBuffer.Reset (allocator.Allocate ((kTableSize + 2) * sizeof (real32))); + + fTable = fBuffer->Buffer_real32 (); + + if (subSample) + { + + fTable [0 ] = (real32) function.Evaluate (0.0); + fTable [kTableSize] = (real32) function.Evaluate (1.0); + + real32 maxDelta = Max_real32 (Abs_real32 (fTable [kTableSize] - + fTable [0 ]), 1.0f) * + (1.0f / 256.0f); + + SubDivide (function, + 0, + kTableSize, + maxDelta); + + } + + else + { + + for (uint32 j = 0; j <= kTableSize; j++) + { + + real64 x = j * (1.0 / (real64) kTableSize); + + real64 y = function.Evaluate (x); + + fTable [j] = (real32) y; + + } + + } + + fTable [kTableSize + 1] = fTable [kTableSize]; + + } + +/*****************************************************************************/ + +void dng_1d_table::Expand16 (uint16 *table16) const + { + + real64 step = (real64) kTableSize / 65535.0; + + real64 y0 = fTable [0]; + real64 y1 = fTable [1]; + + real64 base = y0 * 65535.0 + 0.5; + real64 slope = (y1 - y0) * 65535.0; + + uint32 index = 1; + real64 fract = 0.0; + + for (uint32 j = 0; j < 0x10000; j++) + { + + table16 [j] = (uint16) (base + slope * fract); + + fract += step; + + if (fract > 1.0) + { + + index += 1; + fract -= 1.0; + + y0 = y1; + y1 = fTable [index]; + + base = y0 * 65535.0 + 0.5; + slope = (y1 - y0) * 65535.0; + + } + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_1d_table.h b/source/lib/dng_sdk/dng_1d_table.h new file mode 100644 index 0000000..e24972a --- /dev/null +++ b/source/lib/dng_sdk/dng_1d_table.h @@ -0,0 +1,122 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_1d_table.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Definition of a lookup table based 1D floating-point to floating-point function abstraction using linear interpolation. + */ + +/*****************************************************************************/ + +#ifndef __dng_1d_table__ +#define __dng_1d_table__ + +/*****************************************************************************/ + +#include "dng_assertions.h" +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief A 1D floating-point lookup table using linear interpolation. + +class dng_1d_table + { + + public: + + /// Constants denoting size of table. + + enum + { + kTableBits = 12, //< Table is always a power of 2 in size. This is log2(kTableSize). + kTableSize = (1 << kTableBits) //< Number of entries in table. + }; + + protected: + + AutoPtr fBuffer; + + real32 *fTable; + + public: + + dng_1d_table (); + + virtual ~dng_1d_table (); + + /// Set up table, initialize entries using functiion. + /// This method can throw an exception, e.g. if there is not enough memory. + /// \param allocator Memory allocator from which table memory is allocated. + /// \param function Table is initialized with values of finction.Evalluate(0.0) to function.Evaluate(1.0). + /// \param subSample If true, only sample the function a limited number of times and interpolate. + + void Initialize (dng_memory_allocator &allocator, + const dng_1d_function &function, + bool subSample = false); + + /// Lookup and interpolate mapping for an input. + /// \param x value from 0.0 to 1.0 used as input for mapping + /// \retval Approximation of function.Evaluate(x) + + real32 Interpolate (real32 x) const + { + + real32 y = x * (real32) kTableSize; + + int32 index = (int32) y; + + DNG_ASSERT (index >= 0 && index <= kTableSize, + "dng_1d_table::Interpolate parameter out of range"); + + real32 z = (real32) index; + + real32 fract = y - z; + + return fTable [index ] * (1.0f - fract) + + fTable [index + 1] * ( fract); + + } + + /// Direct access function for table data. + + const real32 * Table () const + { + return fTable; + } + + /// Expand the table to a 16-bit to 16-bit table. + + void Expand16 (uint16 *table16) const; + + private: + + void SubDivide (const dng_1d_function &function, + uint32 lower, + uint32 upper, + real32 maxDelta); + + // Hidden copy constructor and assignment operator. + + dng_1d_table (const dng_1d_table &table); + + dng_1d_table & operator= (const dng_1d_table &table); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_abort_sniffer.cpp b/source/lib/dng_sdk/dng_abort_sniffer.cpp new file mode 100644 index 0000000..4c65733 --- /dev/null +++ b/source/lib/dng_sdk/dng_abort_sniffer.cpp @@ -0,0 +1,242 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_abort_sniffer.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_abort_sniffer.h" + +#include "dng_mutex.h" + +/*****************************************************************************/ + +#if qDNGThreadSafe + +/*****************************************************************************/ + +class dng_priority_manager + { + + private: + + dng_mutex fMutex; + + dng_condition fCondition; + + uint32 fCounter [dng_priority_count]; + + public: + + dng_priority_manager (); + + void Increment (dng_priority priority); + + void Decrement (dng_priority priority); + + void Wait (dng_priority priority); + + private: + + dng_priority MinPriority () + { + + // Assumes mutex is locked. + + for (uint32 level = dng_priority_maximum; + level > dng_priority_minimum; + level--) + { + + if (fCounter [level]) + { + return (dng_priority) level; + } + + } + + return dng_priority_minimum; + + } + + }; + +/*****************************************************************************/ + +dng_priority_manager::dng_priority_manager () + + : fMutex ("dng_priority_manager::fMutex") + , fCondition () + + { + + for (uint32 level = dng_priority_minimum; + level <= dng_priority_maximum; + level++) + { + + fCounter [level] = 0; + + } + + } + +/*****************************************************************************/ + +void dng_priority_manager::Increment (dng_priority priority) + { + + dng_lock_mutex lock (&fMutex); + + fCounter [priority] += 1; + + } + +/*****************************************************************************/ + +void dng_priority_manager::Decrement (dng_priority priority) + { + + dng_lock_mutex lock (&fMutex); + + dng_priority oldMin = MinPriority (); + + fCounter [priority] -= 1; + + dng_priority newMin = MinPriority (); + + if (newMin < oldMin) + { + + fCondition.Broadcast (); + + } + + } + +/*****************************************************************************/ + +void dng_priority_manager::Wait (dng_priority priority) + { + + if (priority < dng_priority_maximum) + { + + dng_lock_mutex lock (&fMutex); + + while (priority < MinPriority ()) + { + + fCondition.Wait (fMutex); + + } + + } + + } + +/*****************************************************************************/ + +static dng_priority_manager gPriorityManager; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +dng_set_minimum_priority::dng_set_minimum_priority (dng_priority priority) + + : fPriority (priority) + + { + + #if qDNGThreadSafe + + gPriorityManager.Increment (fPriority); + + #endif + + } + +/*****************************************************************************/ + +dng_set_minimum_priority::~dng_set_minimum_priority () + { + + #if qDNGThreadSafe + + gPriorityManager.Decrement (fPriority); + + #endif + + } + +/*****************************************************************************/ + +dng_abort_sniffer::dng_abort_sniffer () + + : fPriority (dng_priority_maximum) + + { + + } + +/*****************************************************************************/ + +dng_abort_sniffer::~dng_abort_sniffer () + { + + } + +/*****************************************************************************/ + +void dng_abort_sniffer::SniffForAbort (dng_abort_sniffer *sniffer) + { + + if (sniffer) + { + + #if qDNGThreadSafe + + gPriorityManager.Wait (sniffer->Priority ()); + + #endif + + sniffer->Sniff (); + + } + + } + +/*****************************************************************************/ + +void dng_abort_sniffer::StartTask (const char * /* name */, + real64 /* fract */) + { + + } + +/*****************************************************************************/ + +void dng_abort_sniffer::EndTask () + { + + } + +/*****************************************************************************/ + +void dng_abort_sniffer::UpdateProgress (real64 /* fract */) + { + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_abort_sniffer.h b/source/lib/dng_sdk/dng_abort_sniffer.h new file mode 100644 index 0000000..3a2f553 --- /dev/null +++ b/source/lib/dng_sdk/dng_abort_sniffer.h @@ -0,0 +1,244 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_abort_sniffer.h#2 $ */ +/* $DateTime: 2012/07/11 10:36:56 $ */ +/* $Change: 838485 $ */ +/* $Author: tknoll $ */ + +/** \file + * Classes supporting user cancellation and progress tracking. + */ + +/*****************************************************************************/ + +#ifndef __dng_abort_sniffer__ +#define __dng_abort_sniffer__ + +/*****************************************************************************/ + +#include "dng_flags.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief Thread priority level. + +enum dng_priority + { + + dng_priority_low, + dng_priority_medium, + dng_priority_high, + + dng_priority_count, + + dng_priority_minimum = dng_priority_low, + dng_priority_maximum = dng_priority_high + + }; + +/*****************************************************************************/ + +/// \brief Convenience class for setting thread priority level to minimum. + +class dng_set_minimum_priority + { + + private: + + dng_priority fPriority; + + public: + + dng_set_minimum_priority (dng_priority priority); + + ~dng_set_minimum_priority (); + + }; + +/*****************************************************************************/ + +/** \brief Class for signaling user cancellation and receiving progress updates. + * + * DNG SDK clients should derive a host application specific implementation + * from this class. + */ + +class dng_abort_sniffer + { + + friend class dng_sniffer_task; + + private: + + dng_priority fPriority; + + public: + + dng_abort_sniffer (); + + virtual ~dng_abort_sniffer (); + + /// Getter for priority level. + + dng_priority Priority () const + { + return fPriority; + } + + /// Setter for priority level. + + void SetPriority (dng_priority priority) + { + fPriority = priority; + } + + /// Check for pending user cancellation or other abort. ThrowUserCanceled + /// will be called if one is pending. This static method is provided as a + /// convenience for quickly testing for an abort and throwing an exception + /// if one is pending. + /// \param sniffer The dng_sniffer to test for a pending abort. Can be NULL, + /// in which case there an abort is never signalled. + + static void SniffForAbort (dng_abort_sniffer *sniffer); + + // A way to call Sniff while bypassing the priority wait. + + void SniffNoPriorityWait () + { + Sniff (); + } + + // Specifies whether or not the sniffer may be called by multiple threads + // in parallel. Default result is false. Subclass must override to return + // true. + + virtual bool ThreadSafe () const + { + return false; + } + + protected: + + /// Should be implemented by derived classes to check for an user + /// cancellation. + + virtual void Sniff () = 0; + + /// Signals the start of a named task withn processing in the DNG SDK. + /// Tasks may be nested. + /// \param name of the task + /// \param fract Percentage of total processing this task is expected to + /// take. From 0.0 to 1.0 . + + virtual void StartTask (const char *name, + real64 fract); + + /// Signals the end of the innermost task that has been started. + + virtual void EndTask (); + + /// Signals progress made on current task. + /// \param fract percentage of processing completed on current task. + /// From 0.0 to 1.0 . + + virtual void UpdateProgress (real64 fract); + + }; + +/******************************************************************************/ + +/// \brief Class to establish scope of a named subtask in DNG processing. +/// +/// Instances of this class are intended to be stack allocated. + +class dng_sniffer_task + { + + private: + + dng_abort_sniffer *fSniffer; + + public: + + /// Inform a sniffer of a subtask in DNG processing. + /// \param sniffer The sniffer associated with the host on which this + /// processing is occurring. + /// \param name The name of this subtask as a NUL terminated string. + /// \param fract Percentage of total processing this task is expected + /// to take, from 0.0 to 1.0 . + + dng_sniffer_task (dng_abort_sniffer *sniffer, + const char *name = NULL, + real64 fract = 0.0) + + : fSniffer (sniffer) + + { + if (fSniffer) + fSniffer->StartTask (name, fract); + } + + ~dng_sniffer_task () + { + if (fSniffer) + fSniffer->EndTask (); + } + + /// Check for pending user cancellation or other abort. ThrowUserCanceled + /// will be called if one is pending. + + void Sniff () + { + dng_abort_sniffer::SniffForAbort (fSniffer); + } + + /// Update progress on this subtask. + /// \param fract Percentage of processing completed on current task, + /// from 0.0 to 1.0 . + + void UpdateProgress (real64 fract) + { + if (fSniffer) + fSniffer->UpdateProgress (fract); + } + + /// Update progress on this subtask. + /// \param done Amount of task completed in arbitrary integer units. + /// \param total Total size of task in same arbitrary integer units as done. + + void UpdateProgress (uint32 done, + uint32 total) + { + UpdateProgress ((real64) done / + (real64) total); + } + + /// Signal task completed for progress purposes. + + void Finish () + { + UpdateProgress (1.0); + } + + private: + + // Hidden copy constructor and assignment operator. + + dng_sniffer_task (const dng_sniffer_task &task); + + dng_sniffer_task & operator= (const dng_sniffer_task &task); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_area_task.cpp b/source/lib/dng_sdk/dng_area_task.cpp new file mode 100644 index 0000000..25b100e --- /dev/null +++ b/source/lib/dng_sdk/dng_area_task.cpp @@ -0,0 +1,270 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_area_task.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_area_task.h" + +#include "dng_abort_sniffer.h" +#include "dng_flags.h" +#include "dng_sdk_limits.h" +#include "dng_tile_iterator.h" +#include "dng_utils.h" + +#if qImagecore +extern bool gPrintTimings; +#endif + +/*****************************************************************************/ + +dng_area_task::dng_area_task () + + : fMaxThreads (kMaxMPThreads) + + , fMinTaskArea (256 * 256) + + , fUnitCell (1, 1) + + , fMaxTileSize (256, 256) + + { + + } + +/*****************************************************************************/ + +dng_area_task::~dng_area_task () + { + + } + +/*****************************************************************************/ + +dng_rect dng_area_task::RepeatingTile1 () const + { + + return dng_rect (); + + } + +/*****************************************************************************/ + +dng_rect dng_area_task::RepeatingTile2 () const + { + + return dng_rect (); + + } + +/*****************************************************************************/ + +dng_rect dng_area_task::RepeatingTile3 () const + { + + return dng_rect (); + + } + +/*****************************************************************************/ + +void dng_area_task::Start (uint32 /* threadCount */, + const dng_point & /* tileSize */, + dng_memory_allocator * /* allocator */, + dng_abort_sniffer * /* sniffer */) + { + + } + +/*****************************************************************************/ + +void dng_area_task::Finish (uint32 /* threadCount */) + { + + } + +/*****************************************************************************/ + +dng_point dng_area_task::FindTileSize (const dng_rect &area) const + { + + dng_rect repeatingTile1 = RepeatingTile1 (); + dng_rect repeatingTile2 = RepeatingTile2 (); + dng_rect repeatingTile3 = RepeatingTile3 (); + + if (repeatingTile1.IsEmpty ()) + { + repeatingTile1 = area; + } + + if (repeatingTile2.IsEmpty ()) + { + repeatingTile2 = area; + } + + if (repeatingTile3.IsEmpty ()) + { + repeatingTile3 = area; + } + + uint32 repeatV = Min_uint32 (Min_uint32 (repeatingTile1.H (), + repeatingTile2.H ()), + repeatingTile3.H ()); + + uint32 repeatH = Min_uint32 (Min_uint32 (repeatingTile1.W (), + repeatingTile2.W ()), + repeatingTile3.W ()); + + dng_point maxTileSize = MaxTileSize (); + + dng_point tileSize; + + tileSize.v = Min_int32 (repeatV, maxTileSize.v); + tileSize.h = Min_int32 (repeatH, maxTileSize.h); + + // What this is doing is, if the smallest repeating image tile is larger than the + // maximum tile size, adjusting the tile size down so that the tiles are as small + // as possible while still having the same number of tiles covering the + // repeat area. This makes the areas more equal in size, making MP + // algorithms work better. + + // The image core team did not understand this code, and disabled it. + // Really stupid idea to turn off code you don't understand! + // I'm undoing this removal, because I think the code is correct and useful. + + uint32 countV = (repeatV + tileSize.v - 1) / tileSize.v; + uint32 countH = (repeatH + tileSize.h - 1) / tileSize.h; + + tileSize.v = (repeatV + countV - 1) / countV; + tileSize.h = (repeatH + countH - 1) / countH; + + // Round up to unit cell size. + + dng_point unitCell = UnitCell (); + + if (unitCell.h != 1 || unitCell.v != 1) + { + tileSize.v = ((tileSize.v + unitCell.v - 1) / unitCell.v) * unitCell.v; + tileSize.h = ((tileSize.h + unitCell.h - 1) / unitCell.h) * unitCell.h; + } + + // But if that is larger than maximum tile size, round down to unit cell size. + + if (tileSize.v > maxTileSize.v) + { + tileSize.v = (maxTileSize.v / unitCell.v) * unitCell.v; + } + + if (tileSize.h > maxTileSize.h) + { + tileSize.h = (maxTileSize.h / unitCell.h) * unitCell.h; + } + + #if qImagecore + if (gPrintTimings) + { + fprintf (stdout, "\nRender tile for below: %d x %d\n", (int32) tileSize.h, (int32) tileSize.v); + } + #endif + + return tileSize; + + } + +/*****************************************************************************/ + +void dng_area_task::ProcessOnThread (uint32 threadIndex, + const dng_rect &area, + const dng_point &tileSize, + dng_abort_sniffer *sniffer) + { + + dng_rect repeatingTile1 = RepeatingTile1 (); + dng_rect repeatingTile2 = RepeatingTile2 (); + dng_rect repeatingTile3 = RepeatingTile3 (); + + if (repeatingTile1.IsEmpty ()) + { + repeatingTile1 = area; + } + + if (repeatingTile2.IsEmpty ()) + { + repeatingTile2 = area; + } + + if (repeatingTile3.IsEmpty ()) + { + repeatingTile3 = area; + } + + dng_rect tile1; + + dng_tile_iterator iter1 (repeatingTile3, area); + + while (iter1.GetOneTile (tile1)) + { + + dng_rect tile2; + + dng_tile_iterator iter2 (repeatingTile2, tile1); + + while (iter2.GetOneTile (tile2)) + { + + dng_rect tile3; + + dng_tile_iterator iter3 (repeatingTile1, tile2); + + while (iter3.GetOneTile (tile3)) + { + + dng_rect tile4; + + dng_tile_iterator iter4 (tileSize, tile3); + + while (iter4.GetOneTile (tile4)) + { + + dng_abort_sniffer::SniffForAbort (sniffer); + + Process (threadIndex, tile4, sniffer); + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +void dng_area_task::Perform (dng_area_task &task, + const dng_rect &area, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer) + { + + dng_point tileSize (task.FindTileSize (area)); + + task.Start (1, tileSize, allocator, sniffer); + + task.ProcessOnThread (0, area, tileSize, sniffer); + + task.Finish (1); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_area_task.h b/source/lib/dng_sdk/dng_area_task.h new file mode 100644 index 0000000..4824873 --- /dev/null +++ b/source/lib/dng_sdk/dng_area_task.h @@ -0,0 +1,198 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_area_task.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Class to handle partitioning a rectangular image processing operation taking into account multiple processing resources and memory constraints. + */ + +/*****************************************************************************/ + +#ifndef __dng_area_task__ +#define __dng_area_task__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_point.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief Abstract class for rectangular processing operations with support for partitioning across multiple processing resources and observing memory constraints. + +class dng_area_task + { + + protected: + + uint32 fMaxThreads; + + uint32 fMinTaskArea; + + dng_point fUnitCell; + + dng_point fMaxTileSize; + + public: + + dng_area_task (); + + virtual ~dng_area_task (); + + /// Getter for the maximum number of threads (resources) that can be used for processing + /// + /// \retval Number of threads, minimum of 1, that can be used for this task. + + virtual uint32 MaxThreads () const + { + return fMaxThreads; + } + + /// Getter for minimum area of a partitioned rectangle. + /// Often it is not profitable to use more resources if it requires partitioning the input into chunks that are too small, + /// as the overhead increases more than the speedup. This method can be ovreridden for a specific task to indicate the smallest + /// area for partitioning. Default is 256x256 pixels. + /// + /// \retval Minimum area for a partitoned tile in order to give performant operation. (Partitions can be smaller due to small inputs and edge cases.) + + virtual uint32 MinTaskArea () const + { + return fMinTaskArea; + } + + /// Getter for dimensions of which partitioned tiles should be a multiple. + /// Various methods of processing prefer certain alignments. The partitioning attempts to construct tiles such that the + /// sizes are a multiple of the dimensions of this point. + /// + /// \retval a point giving preferred alignment in x and y + + virtual dng_point UnitCell () const + { + return fUnitCell; + } + + /// Getter for maximum size of a tile for processing. + /// Often processing will need to allocate temporary buffers or use other resources that are either fixed or in limited supply. + /// The maximum tile size forces further partitioning if the tile is bigger than this size. + /// + /// \retval Maximum tile size allowed for this area task. + + virtual dng_point MaxTileSize () const + { + return fMaxTileSize; + } + + /// Getter for RepeatingTile1. + /// RepeatingTile1, RepeatingTile2, and RepeatingTile3 are used to establish a set of 0 to 3 tile patterns for which + /// the resulting partitions that the final Process method is called on will not cross tile boundaries in any of the + /// tile patterns. This can be used for a processing routine that needs to read from two tiles and write to a third + /// such that all the tiles are aligned and sized in a certain way. A RepeatingTile value is valid if it is non-empty. + /// Higher numbered RepeatingTile patterns are only used if all lower ones are non-empty. A RepeatingTile pattern must + /// be a multiple of UnitCell in size for all constraints of the partitionerr to be met. + + virtual dng_rect RepeatingTile1 () const; + + /// Getter for RepeatingTile2. + /// RepeatingTile1, RepeatingTile2, and RepeatingTile3 are used to establish a set of 0 to 3 tile patterns for which + /// the resulting partitions that the final Process method is called on will not cross tile boundaries in any of the + /// tile patterns. This can be used for a processing routine that needs to read from two tiles and write to a third + /// such that all the tiles are aligned and sized in a certain way. A RepeatingTile value is valid if it is non-empty. + /// Higher numbered RepeatingTile patterns are only used if all lower ones are non-empty. A RepeatingTile pattern must + /// be a multiple of UnitCell in size for all constraints of the partitionerr to be met. + + virtual dng_rect RepeatingTile2 () const; + + /// Getter for RepeatingTile3. + /// RepeatingTile1, RepeatingTile2, and RepeatingTile3 are used to establish a set of 0 to 3 tile patterns for which + /// the resulting partitions that the final Process method is called on will not cross tile boundaries in any of the + /// tile patterns. This can be used for a processing routine that needs to read from two tiles and write to a third + /// such that all the tiles are aligned and sized in a certain way. A RepeatingTile value is valid if it is non-empty. + /// Higher numbered RepeatingTile patterns are only used if all lower ones are non-empty. A RepeatingTile pattern must + /// be a multiple of UnitCell in size for all constraints of the partitionerr to be met. + + virtual dng_rect RepeatingTile3 () const; + + /// Task startup method called before any processing is done on partitions. + /// The Start method is called before any processing is done and can be overridden to allocate temporary buffers, etc. + /// + /// \param threadCount Total number of threads that will be used for processing. Less than or equal to MaxThreads. + /// \param tileSize Size of source tiles which will be processed. (Not all tiles will be this size due to edge conditions.) + /// \param allocator dng_memory_allocator to use for allocating temporary buffers, etc. + /// \param sniffer Sniffer to test for user cancellation and to set up progress. + + virtual void Start (uint32 threadCount, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer); + + /// Process one tile or fully partitioned area. + /// This method is overridden by derived classes to implement the actual image processing. Note that the sniffer can be ignored if it is certain that a + /// processing task will complete very quickly. + /// This method should never be called directly but rather accessed via Process. + /// There is no allocator parameter as all allocation should be done in Start. + /// + /// \param threadIndex 0 to threadCount - 1 index indicating which thread this is. (Can be used to get a thread-specific buffer allocated in the Start method.) + /// \param tile Area to process. + /// \param sniffer dng_abort_sniffer to use to check for user cancellation and progress updates. + + virtual void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer *sniffer) = 0; + + /// Task computation finalization and teardown method. + /// Called after all resources have completed processing. Can be overridden to accumulate results and free resources allocated in Start. + /// + /// \param threadCount Number of threads used for processing. Same as value passed to Start. + + virtual void Finish (uint32 threadCount); + + /// Find tile size taking into account repeating tiles, unit cell, and maximum tile size. + /// \param area Computation area for which to find tile size. + /// \retval Tile size as height and width in point. + + dng_point FindTileSize (const dng_rect &area) const; + + /// Handle one resource's worth of partitioned tiles. + /// Called after thread partitioning has already been done. Area may be further subdivided to handle maximum tile size, etc. + /// It will be rare to override this method. + /// + /// \param threadIndex 0 to threadCount - 1 index indicating which thread this is. + /// \param area Tile area partitioned to this resource. + /// \param tileSize + /// \param sniffer dng_abort_sniffer to use to check for user cancellation and progress updates. + + void ProcessOnThread (uint32 threadIndex, + const dng_rect &area, + const dng_point &tileSize, + dng_abort_sniffer *sniffer); + + /// Default resource partitioner that assumes a single resource to be used for processing. + /// Implementations that are aware of multiple processing resources should override (replace) this method. + /// This is usually done in dng_host::PerformAreaTask . + /// \param task The task to perform. + /// \param area The area on which mage processing should be performed. + /// \param allocator dng_memory_allocator to use for allocating temporary buffers, etc. + /// \param sniffer dng_abort_sniffer to use to check for user cancellation and progress updates. + + static void Perform (dng_area_task &task, + const dng_rect &area, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_assertions.h b/source/lib/dng_sdk/dng_assertions.h new file mode 100644 index 0000000..3c45957 --- /dev/null +++ b/source/lib/dng_sdk/dng_assertions.h @@ -0,0 +1,133 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_assertions.h#3 $ */ +/* $DateTime: 2012/09/05 12:31:51 $ */ +/* $Change: 847652 $ */ +/* $Author: tknoll $ */ + +/** \file + * Conditionally compiled assertion check support. + */ + +/*****************************************************************************/ + +#ifndef __dng_assertions__ +#define __dng_assertions__ + +/*****************************************************************************/ + +#include "dng_exceptions.h" +#include "dng_flags.h" + +/*****************************************************************************/ + +#if qDNGDebug + +/// Platform-specific function to display an assert. + +void dng_show_message (const char *s); + +/// Show a formatted error message. + +void dng_show_message_f (const char *fmt, ...); + +#endif + +/*****************************************************************************/ + +#ifndef DNG_ASSERT + +#if qDNGDebug + +/// Conditionally compiled macro to check an assertion and display a message if +/// it fails and assertions are compiled in via qDNGDebug +/// \param x Predicate which must be true. +/// \param y String to display if x is not true. + +#define DNG_ASSERT(x,y) { if (!(x)) dng_show_message (y); } + +#else + +/// Conditionally compiled macro to check an assertion and display a message if +/// it fails and assertions are compiled in via qDNGDebug +/// \param x Predicate which must be true. +/// \param y String to display if x is not true. + +#define DNG_ASSERT(x,y) + +#endif +#endif + +/*****************************************************************************/ + +#ifndef DNG_REQUIRE + +#if qDNGDebug + +/// Conditionally compiled macro to check an assertion, display a message, and throw +/// an exception if it fails and assertions are compiled in via qDNGDebug +/// \param condition Predicate which must be true. +/// \param msg String to display if condition is not true. + +#define DNG_REQUIRE(condition,msg) \ + do \ + { \ + \ + if (!(condition)) \ + { \ + \ + DNG_ASSERT(condition, msg); \ + \ + ThrowProgramError (msg); \ + \ + } \ + \ + } \ + while (0) + +#else + +/// Conditionally compiled macro to check an assertion, display a message, and throw +/// an exception if it fails and assertions are compiled in via qDNGDebug +/// \param condition Predicate which must be true. +/// \param msg String to display if condition is not true. + +#define DNG_REQUIRE(condition,msg) \ + do \ + { \ + \ + if (!(condition)) \ + { \ + \ + ThrowProgramError (msg); \ + \ + } \ + \ + } \ + while (0) + +#endif +#endif + +/*****************************************************************************/ + +#ifndef DNG_REPORT + +/// Macro to display an informational message +/// \param x String to display. + +#define DNG_REPORT(x) DNG_ASSERT (false, x) + +#endif + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_auto_ptr.h b/source/lib/dng_sdk/dng_auto_ptr.h new file mode 100644 index 0000000..02154be --- /dev/null +++ b/source/lib/dng_sdk/dng_auto_ptr.h @@ -0,0 +1,259 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_auto_ptr.h#2 $ */ +/* $DateTime: 2012/07/11 10:36:56 $ */ +/* $Change: 838485 $ */ +/* $Author: tknoll $ */ + +/** \file + * Class to implement std::auto_ptr like functionality even on platforms which do not + * have a full Standard C++ library. + */ + +/*****************************************************************************/ + +#ifndef __dng_auto_ptr__ +#define __dng_auto_ptr__ + +#include + +/*****************************************************************************/ + +// The following template has similar functionality to the STL auto_ptr, without +// requiring all the weight of STL. + +/*****************************************************************************/ + +/// \brief A class intended to be used in stack scope to hold a pointer from new. The +/// held pointer will be deleted automatically if the scope is left without calling +/// Release on the AutoPtr first. + +template +class AutoPtr + { + + private: + + T *p_; + + public: + + /// Construct an AutoPtr with no referent. + + AutoPtr () : p_ (0) { } + + /// Construct an AutoPtr which owns the argument pointer. + /// \param p pointer which constructed AutoPtr takes ownership of. p will be + /// deleted on destruction or Reset unless Release is called first. + + explicit AutoPtr (T *p) : p_( p ) { } + + /// Reset is called on destruction. + + ~AutoPtr (); + + /// Call Reset with a pointer from new. Uses T's default constructor. + + void Alloc (); + + /// Return the owned pointer of this AutoPtr, NULL if none. No change in + /// ownership or other effects occur. + + T *Get () const { return p_; } + + /// Return the owned pointer of this AutoPtr, NULL if none. The AutoPtr gives + /// up ownership and takes NULL as its value. + + T *Release (); + + /// If a pointer is owned, it is deleted. Ownership is taken of passed in + /// pointer. + /// \param p pointer which constructed AutoPtr takes ownership of. p will be + /// deleted on destruction or Reset unless Release is called first. + + void Reset (T *p); + + /// If a pointer is owned, it is deleted and the AutoPtr takes NULL as its + /// value. + + void Reset (); + + /// Allows members of the owned pointer to be accessed directly. It is an + /// error to call this if the AutoPtr has NULL as its value. + + T *operator-> () const { return p_; } + + /// Returns a reference to the object that the owned pointer points to. It is + /// an error to call this if the AutoPtr has NULL as its value. + + T &operator* () const { return *p_; } + + /// Swap with another auto ptr. + + friend inline void Swap (AutoPtr< T > &x, AutoPtr< T > &y) + { + T* temp = x.p_; + x.p_ = y.p_; + y.p_ = temp; + } + + private: + + // Hidden copy constructor and assignment operator. I don't think the STL + // "feature" of grabbing ownership of the pointer is a good idea. + + AutoPtr (AutoPtr &rhs); + + AutoPtr & operator= (AutoPtr &rhs); + + }; + +/*****************************************************************************/ + +template +AutoPtr::~AutoPtr () + { + + delete p_; + p_ = 0; + + } + +/*****************************************************************************/ + +template +T *AutoPtr::Release () + { + T *result = p_; + p_ = 0; + return result; + } + +/*****************************************************************************/ + +template +void AutoPtr::Reset (T *p) + { + + if (p_ != p) + { + if (p_ != 0) + delete p_; + p_ = p; + } + + } + +/*****************************************************************************/ + +template +void AutoPtr::Reset () + { + + if (p_ != 0) + { + delete p_; + p_ = 0; + } + + } + +/*****************************************************************************/ + +template +void AutoPtr::Alloc () + { + this->Reset (new T); + } + +/*****************************************************************************/ + +/// \brief A class intended to be used similarly to AutoPtr but for arrays. + +template +class AutoArray + { + + public: + + /// Construct an AutoArray which owns the argument pointer. + /// \param p array pointer which constructed AutoArray takes ownership of. p + /// will be deleted on destruction or Reset unless Release is called first. + + explicit AutoArray (T *p_ = 0) : p (p_) { } + + /// Reset is called on destruction. + + ~AutoArray () + { + delete [] p; + p = 0; + } + + /// Return the owned array pointer of this AutoArray, NULL if none. The + /// AutoArray gives up ownership and takes NULL as its value. + + T *Release () + { + T *p_ = p; + p = 0; + return p_; + } + + /// If a pointer is owned, it is deleted. Ownership is taken of passed in + /// pointer. + /// \param p array pointer which constructed AutoArray takes ownership of. p + /// will be deleted on destruction or Reset unless Release is called first. + + void Reset (T *p_ = 0) + { + if (p != p_) + { + delete [] p; + p = p_; + } + } + + /// Allows indexing into the AutoArray. It is an error to call this if the + /// AutoArray has NULL as its value. + + T &operator[] (ptrdiff_t i) const + { + return p [i]; + } + + /// Return the owned pointer of this AutoArray, NULL if none. No change in + /// ownership or other effects occur. + + T *Get () const + { + return p; + } + + private: + + // Hidden copy constructor and assignment operator. + + AutoArray (const AutoArray &); + + const AutoArray & operator= (const AutoArray &); + + private: + + // Owned pointer or NULL. + + T *p; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_bad_pixels.cpp b/source/lib/dng_sdk/dng_bad_pixels.cpp new file mode 100644 index 0000000..920acd6 --- /dev/null +++ b/source/lib/dng_sdk/dng_bad_pixels.cpp @@ -0,0 +1,1856 @@ +/*****************************************************************************/ +// Copyright 2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_bad_pixels.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_bad_pixels.h" + +#include "dng_filter_task.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_negative.h" + +#include + +/*****************************************************************************/ + +dng_opcode_FixBadPixelsConstant::dng_opcode_FixBadPixelsConstant + (uint32 constant, + uint32 bayerPhase) + + : dng_filter_opcode (dngOpcode_FixBadPixelsConstant, + dngVersion_1_3_0_0, + 0) + + , fConstant (constant) + , fBayerPhase (bayerPhase) + + { + + } + +/*****************************************************************************/ + +dng_opcode_FixBadPixelsConstant::dng_opcode_FixBadPixelsConstant + (dng_stream &stream) + + : dng_filter_opcode (dngOpcode_FixBadPixelsConstant, + stream, + "FixBadPixelsConstant") + + , fConstant (0) + , fBayerPhase (0) + + { + + if (stream.Get_uint32 () != 8) + { + ThrowBadFormat (); + } + + fConstant = stream.Get_uint32 (); + fBayerPhase = stream.Get_uint32 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Constant: %u\n", (unsigned) fConstant); + + printf ("Bayer Phase: %u\n", (unsigned) fBayerPhase); + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsConstant::PutData (dng_stream &stream) const + { + + stream.Put_uint32 (8); + + stream.Put_uint32 (fConstant ); + stream.Put_uint32 (fBayerPhase); + + } + +/*****************************************************************************/ + +dng_point dng_opcode_FixBadPixelsConstant::SrcRepeat () + { + + return dng_point (2, 2); + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_FixBadPixelsConstant::SrcArea (const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect srcArea = dstArea; + + srcArea.t -= 2; + srcArea.l -= 2; + + srcArea.b += 2; + srcArea.r += 2; + + return srcArea; + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsConstant::Prepare (dng_negative & /* negative */, + uint32 /* threadCount */, + const dng_point & /* tileSize */, + const dng_rect & /* imageBounds */, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator & /* allocator */) + { + + // This opcode is restricted to single channel images. + + if (imagePlanes != 1) + { + + ThrowBadFormat (); + + } + + // This opcode is restricted to 16-bit images. + + if (bufferPixelType != ttShort) + { + + ThrowBadFormat (); + + } + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsConstant::ProcessArea (dng_negative & /* negative */, + uint32 /* threadIndex */, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dstBuffer.CopyArea (srcBuffer, + dstArea, + 0, + dstBuffer.fPlanes); + + uint16 badPixel = (uint16) fConstant; + + for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++) + { + + const uint16 *sPtr = srcBuffer.ConstPixel_uint16 (dstRow, dstArea.l, 0); + uint16 *dPtr = dstBuffer.DirtyPixel_uint16 (dstRow, dstArea.l, 0); + + for (int32 dstCol = dstArea.l; dstCol < dstArea.r; dstCol++) + { + + if (*sPtr == badPixel) + { + + uint32 count = 0; + uint32 total = 0; + + uint16 value; + + if (IsGreen (dstRow, dstCol)) // Green pixel + { + + value = sPtr [-srcBuffer.fRowStep - 1]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + value = sPtr [-srcBuffer.fRowStep + 1]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + value = sPtr [srcBuffer.fRowStep - 1]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + value = sPtr [srcBuffer.fRowStep + 1]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + } + + else // Red/blue pixel. + { + + value = sPtr [-srcBuffer.fRowStep * 2]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + value = sPtr [srcBuffer.fRowStep * 2]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + value = sPtr [-2]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + value = sPtr [2]; + + if (value != badPixel) + { + count += 1; + total += value; + } + + } + + if (count == 4) // Most common case. + { + + *dPtr = (uint16) ((total + 2) >> 2); + + } + + else if (count > 0) + { + + *dPtr = (uint16) ((total + (count >> 1)) / count); + + } + + } + + sPtr++; + dPtr++; + + } + + } + + } + +/*****************************************************************************/ + +dng_bad_pixel_list::dng_bad_pixel_list () + + : fBadPoints () + , fBadRects () + + { + + } + +/*****************************************************************************/ + +void dng_bad_pixel_list::AddPoint (const dng_point &pt) + { + + fBadPoints.push_back (pt); + + } + +/*****************************************************************************/ + +void dng_bad_pixel_list::AddRect (const dng_rect &r) + { + + fBadRects.push_back (r); + + } + +/*****************************************************************************/ + +static bool SortBadPoints (const dng_point &a, + const dng_point &b) + { + + if (a.v < b.v) + return true; + + if (a.v > b.v) + return false; + + return a.h < b.h; + + } + +/*****************************************************************************/ + +static bool SortBadRects (const dng_rect &a, + const dng_rect &b) + { + + if (a.t < b.t) + return true; + + if (a.t > b.t) + return false; + + if (a.l < b.l) + return true; + + if (a.l > b.l) + return false; + + if (a.b < b.b) + return true; + + if (a.b > b.b) + return false; + + return a.r < b.r; + + } + +/*****************************************************************************/ + +void dng_bad_pixel_list::Sort () + { + + if (PointCount () > 1) + { + + std::sort (fBadPoints.begin (), + fBadPoints.end (), + SortBadPoints); + + } + + if (RectCount () > 1) + { + + std::sort (fBadRects.begin (), + fBadRects.end (), + SortBadRects); + + } + + } + +/*****************************************************************************/ + +bool dng_bad_pixel_list::IsPointIsolated (uint32 index, + uint32 radius) const + { + + dng_point pt = Point (index); + + // Search backward through bad point list. + + for (int32 j = index - 1; j >= 0; j--) + { + + const dng_point &pt2 = Point (j); + + if (pt2.v < pt.v - (int32) radius) + { + break; + } + + if (Abs_int32 (pt2.h - pt.h) <= radius) + { + return false; + } + + } + + // Search forward through bad point list. + + for (uint32 k = index + 1; k < PointCount (); k++) + { + + const dng_point &pt2 = Point (k); + + if (pt2.v > pt.v + (int32) radius) + { + break; + } + + if (Abs_int32 (pt2.h - pt.h) <= radius) + { + return false; + } + + } + + // Search through bad rectangle list. + + dng_rect testRect (pt.v - radius, + pt.h - radius, + pt.v + radius + 1, + pt.h + radius + 1); + + for (uint32 n = 0; n < RectCount (); n++) + { + + if ((testRect & Rect (n)).NotEmpty ()) + { + return false; + } + + } + + // Did not find point anywhere, so bad pixel is isolated. + + return true; + + } + +/*****************************************************************************/ + +bool dng_bad_pixel_list::IsRectIsolated (uint32 index, + uint32 radius) const + { + + dng_rect testRect = Rect (index); + + testRect.t -= radius; + testRect.l -= radius; + testRect.b += radius; + testRect.r += radius; + + for (uint32 n = 0; n < RectCount (); n++) + { + + if (n != index) + { + + if ((testRect & Rect (n)).NotEmpty ()) + { + return false; + } + + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_bad_pixel_list::IsPointValid (const dng_point &pt, + const dng_rect &imageBounds, + uint32 index) const + { + + // The point must be in the image bounds to be valid. + + if (pt.v < imageBounds.t || + pt.h < imageBounds.l || + pt.v >= imageBounds.b || + pt.h >= imageBounds.r) + { + return false; + } + + // Only search the bad point list if we have a starting search index. + + if (index != kNoIndex) + { + + // Search backward through bad point list. + + for (int32 j = index - 1; j >= 0; j--) + { + + const dng_point &pt2 = Point (j); + + if (pt2.v < pt.v) + { + break; + } + + if (pt2 == pt) + { + return false; + } + + } + + // Search forward through bad point list. + + for (uint32 k = index + 1; k < PointCount (); k++) + { + + const dng_point &pt2 = Point (k); + + if (pt2.v > pt.v) + { + break; + } + + if (pt2 == pt) + { + return false; + } + + } + + } + + // Search through bad rectangle list. + + for (uint32 n = 0; n < RectCount (); n++) + { + + const dng_rect &r = Rect (n); + + if (pt.v >= r.t && + pt.h >= r.l && + pt.v < r.b && + pt.h < r.r) + { + return false; + } + + } + + // Did not find point anywhere, so pixel is valid. + + return true; + + } + +/*****************************************************************************/ + +dng_opcode_FixBadPixelsList::dng_opcode_FixBadPixelsList + (AutoPtr &list, + uint32 bayerPhase) + + + : dng_filter_opcode (dngOpcode_FixBadPixelsList, + dngVersion_1_3_0_0, + 0) + + , fList () + + , fBayerPhase (bayerPhase) + + { + + fList.Reset (list.Release ()); + + fList->Sort (); + + } + +/*****************************************************************************/ + +dng_opcode_FixBadPixelsList::dng_opcode_FixBadPixelsList (dng_stream &stream) + + : dng_filter_opcode (dngOpcode_FixBadPixelsList, + stream, + "FixBadPixelsList") + + , fList () + + , fBayerPhase (0) + + { + + uint32 size = stream.Get_uint32 (); + + fBayerPhase = stream.Get_uint32 (); + + uint32 pCount = stream.Get_uint32 (); + uint32 rCount = stream.Get_uint32 (); + + if (size != 12 + pCount * 8 + rCount * 16) + { + ThrowBadFormat (); + } + + fList.Reset (new dng_bad_pixel_list); + + uint32 index; + + for (index = 0; index < pCount; index++) + { + + dng_point pt; + + pt.v = stream.Get_int32 (); + pt.h = stream.Get_int32 (); + + fList->AddPoint (pt); + + } + + for (index = 0; index < rCount; index++) + { + + dng_rect r; + + r.t = stream.Get_int32 (); + r.l = stream.Get_int32 (); + r.b = stream.Get_int32 (); + r.r = stream.Get_int32 (); + + fList->AddRect (r); + + } + + fList->Sort (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Bayer Phase: %u\n", (unsigned) fBayerPhase); + + printf ("Bad Pixels: %u\n", (unsigned) pCount); + + for (index = 0; index < pCount && index < gDumpLineLimit; index++) + { + printf (" Pixel [%u]: v=%d, h=%d\n", + (unsigned) index, + (int) fList->Point (index).v, + (int) fList->Point (index).h); + } + + if (pCount > gDumpLineLimit) + { + printf (" ... %u bad pixels skipped\n", (unsigned) (pCount - gDumpLineLimit)); + } + + printf ("Bad Rects: %u\n", (unsigned) rCount); + + for (index = 0; index < rCount && index < gDumpLineLimit; index++) + { + printf (" Rect [%u]: t=%d, l=%d, b=%d, r=%d\n", + (unsigned) index, + (int) fList->Rect (index).t, + (int) fList->Rect (index).l, + (int) fList->Rect (index).b, + (int) fList->Rect (index).r); + } + + if (rCount > gDumpLineLimit) + { + printf (" ... %u bad rects skipped\n", (unsigned) (rCount - gDumpLineLimit)); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::PutData (dng_stream &stream) const + { + + uint32 pCount = fList->PointCount (); + uint32 rCount = fList->RectCount (); + + stream.Put_uint32 (12 + pCount * 8 + rCount * 16); + + stream.Put_uint32 (fBayerPhase); + + stream.Put_uint32 (pCount); + stream.Put_uint32 (rCount); + + uint32 index; + + for (index = 0; index < pCount; index++) + { + + const dng_point &pt (fList->Point (index)); + + stream.Put_int32 (pt.v); + stream.Put_int32 (pt.h); + + } + + for (index = 0; index < rCount; index++) + { + + const dng_rect &r (fList->Rect (index)); + + stream.Put_int32 (r.t); + stream.Put_int32 (r.l); + stream.Put_int32 (r.b); + stream.Put_int32 (r.r); + + } + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_FixBadPixelsList::SrcArea (const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + int32 padding = 0; + + if (fList->PointCount ()) + { + padding += kBadPointPadding; + } + + if (fList->RectCount ()) + { + padding += kBadRectPadding; + } + + dng_rect srcArea = dstArea; + + srcArea.t -= padding; + srcArea.l -= padding; + + srcArea.b += padding; + srcArea.r += padding; + + return srcArea; + + } + +/*****************************************************************************/ + +dng_point dng_opcode_FixBadPixelsList::SrcRepeat () + { + + return dng_point (2, 2); + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::Prepare (dng_negative & /* negative */, + uint32 /* threadCount */, + const dng_point & /* tileSize */, + const dng_rect & /* imageBounds */, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator & /* allocator */) + { + + // This opcode is restricted to single channel images. + + if (imagePlanes != 1) + { + + ThrowBadFormat (); + + } + + // This opcode is restricted to 16-bit images. + + if (bufferPixelType != ttShort) + { + + ThrowBadFormat (); + + } + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::FixIsolatedPixel (dng_pixel_buffer &buffer, + dng_point &badPoint) + { + + uint16 *p0 = buffer.DirtyPixel_uint16 (badPoint.v - 2, badPoint.h - 2, 0); + uint16 *p1 = buffer.DirtyPixel_uint16 (badPoint.v - 1, badPoint.h - 2, 0); + uint16 *p2 = buffer.DirtyPixel_uint16 (badPoint.v , badPoint.h - 2, 0); + uint16 *p3 = buffer.DirtyPixel_uint16 (badPoint.v + 1, badPoint.h - 2, 0); + uint16 *p4 = buffer.DirtyPixel_uint16 (badPoint.v + 2, badPoint.h - 2, 0); + + uint32 est0; + uint32 est1; + uint32 est2; + uint32 est3; + + uint32 grad0; + uint32 grad1; + uint32 grad2; + uint32 grad3; + + if (IsGreen (badPoint.v, badPoint.h)) // Green pixel + { + + // g00 b01 g02 b03 g04 + // r10 g11 r12 g13 r14 + // g20 b21 g22 b23 g24 + // r30 g31 r32 g33 r34 + // g40 b41 g42 b43 g44 + + int32 b01 = p0 [1]; + int32 g02 = p0 [2]; + int32 b03 = p0 [3]; + + int32 r10 = p1 [0]; + int32 g11 = p1 [1]; + int32 r12 = p1 [2]; + int32 g13 = p1 [3]; + int32 r14 = p1 [4]; + + int32 g20 = p2 [0]; + int32 b21 = p2 [1]; + int32 b23 = p2 [3]; + int32 g24 = p2 [4]; + + int32 r30 = p3 [0]; + int32 g31 = p3 [1]; + int32 r32 = p3 [2]; + int32 g33 = p3 [3]; + int32 r34 = p3 [4]; + + int32 b41 = p4 [1]; + int32 g42 = p4 [2]; + int32 b43 = p4 [3]; + + est0 = g02 + g42; + + grad0 = Abs_int32 (g02 - g42) + + Abs_int32 (g11 - g31) + + Abs_int32 (g13 - g33) + + Abs_int32 (b01 - b21) + + Abs_int32 (b03 - b23) + + Abs_int32 (b21 - b41) + + Abs_int32 (b23 - b43); + + est1 = g11 + g33; + + grad1 = Abs_int32 (g11 - g33) + + Abs_int32 (g02 - g24) + + Abs_int32 (g20 - g42) + + Abs_int32 (b01 - b23) + + Abs_int32 (r10 - r32) + + Abs_int32 (r12 - r34) + + Abs_int32 (b21 - b43); + + est2 = g20 + g24; + + grad2 = Abs_int32 (g20 - g24) + + Abs_int32 (g11 - g13) + + Abs_int32 (g31 - g33) + + Abs_int32 (r10 - r12) + + Abs_int32 (r30 - r32) + + Abs_int32 (r12 - r14) + + Abs_int32 (r32 - r34); + + est3 = g13 + g31; + + grad3 = Abs_int32 (g13 - g31) + + Abs_int32 (g02 - g20) + + Abs_int32 (g24 - g42) + + Abs_int32 (b03 - b21) + + Abs_int32 (r14 - r32) + + Abs_int32 (r12 - r30) + + Abs_int32 (b23 - b41); + + } + + else // Red/blue pixel + { + + // b00 g01 b02 g03 b04 + // g10 r11 g12 r13 g14 + // b20 g21 b22 g23 b24 + // g30 r31 g32 r33 g34 + // b40 g41 b42 g43 b44 + + int32 b00 = p0 [0]; + int32 g01 = p0 [1]; + int32 b02 = p0 [2]; + int32 g03 = p0 [3]; + int32 b04 = p0 [4]; + + int32 g10 = p1 [0]; + int32 r11 = p1 [1]; + int32 g12 = p1 [2]; + int32 r13 = p1 [3]; + int32 g14 = p1 [4]; + + int32 b20 = p2 [0]; + int32 g21 = p2 [1]; + int32 g23 = p2 [3]; + int32 b24 = p2 [4]; + + int32 g30 = p3 [0]; + int32 r31 = p3 [1]; + int32 g32 = p3 [2]; + int32 r33 = p3 [3]; + int32 g34 = p3 [4]; + + int32 b40 = p4 [0]; + int32 g41 = p4 [1]; + int32 b42 = p4 [2]; + int32 g43 = p4 [3]; + int32 b44 = p4 [4]; + + est0 = b02 + b42; + + grad0 = Abs_int32 (b02 - b42) + + Abs_int32 (g12 - g32) + + Abs_int32 (g01 - g21) + + Abs_int32 (g21 - g41) + + Abs_int32 (g03 - g23) + + Abs_int32 (g23 - g43) + + Abs_int32 (r11 - r31) + + Abs_int32 (r13 - r33); + + est1 = b00 + b44; + + grad1 = Abs_int32 (b00 - b44) + + Abs_int32 (r11 - r33) + + Abs_int32 (g01 - g23) + + Abs_int32 (g10 - g32) + + Abs_int32 (g12 - g34) + + Abs_int32 (g21 - g43) + + Abs_int32 (b02 - b24) + + Abs_int32 (b20 - b42); + + est2 = b20 + b24; + + grad2 = Abs_int32 (b20 - b24) + + Abs_int32 (g21 - g23) + + Abs_int32 (g10 - g12) + + Abs_int32 (g12 - g14) + + Abs_int32 (g30 - g32) + + Abs_int32 (g32 - g34) + + Abs_int32 (r11 - r13) + + Abs_int32 (r31 - r33); + + est3 = b04 + b40; + + grad3 = Abs_int32 (b04 - b40) + + Abs_int32 (r13 - r31) + + Abs_int32 (g03 - g21) + + Abs_int32 (g14 - g32) + + Abs_int32 (g12 - g30) + + Abs_int32 (g23 - g41) + + Abs_int32 (b02 - b20) + + Abs_int32 (b24 - b42); + + } + + uint32 minGrad = Min_uint32 (grad0, grad1); + + minGrad = Min_uint32 (minGrad, grad2); + minGrad = Min_uint32 (minGrad, grad3); + + uint32 limit = (minGrad * 3) >> 1; + + uint32 total = 0; + uint32 count = 0; + + if (grad0 <= limit) + { + total += est0; + count += 2; + } + + if (grad1 <= limit) + { + total += est1; + count += 2; + } + + if (grad2 <= limit) + { + total += est2; + count += 2; + } + + if (grad3 <= limit) + { + total += est3; + count += 2; + } + + uint32 estimate = (total + (count >> 1)) / count; + + p2 [2] = (uint16) estimate; + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::FixClusteredPixel (dng_pixel_buffer &buffer, + uint32 pointIndex, + const dng_rect &imageBounds) + { + + const uint32 kNumSets = 3; + const uint32 kSetSize = 4; + + static const int32 kOffset [kNumSets] [kSetSize] [2] = + { + { + { -1, 1 }, + { -1, -1 }, + { 1, -1 }, + { 1, 1 } + }, + { + { -2, 0 }, + { 2, 0 }, + { 0, -2 }, + { 0, 2 } + }, + { + { -2, -2 }, + { -2, 2 }, + { 2, -2 }, + { 2, 2 } + } + }; + + dng_point badPoint = fList->Point (pointIndex); + + bool isGreen = IsGreen (badPoint.v, badPoint.h); + + uint16 *p = buffer.DirtyPixel_uint16 (badPoint.v, badPoint.h, 0); + + for (uint32 set = 0; set < kNumSets; set++) + { + + if (!isGreen && (kOffset [set] [0] [0] & 1) == 1) + { + continue; + } + + uint32 total = 0; + uint32 count = 0; + + for (uint32 entry = 0; entry < kSetSize; entry++) + { + + dng_point offset (kOffset [set] [entry] [0], + kOffset [set] [entry] [1]); + + if (fList->IsPointValid (badPoint + offset, + imageBounds, + pointIndex)) + { + + total += p [offset.v * buffer.fRowStep + + offset.h * buffer.fColStep]; + + count++; + + } + + } + + if (count) + { + + uint32 estimate = (total + (count >> 1)) / count; + + p [0] = (uint16) estimate; + + return; + + } + + } + + // Unable to patch bad pixel. Leave pixel as is. + + #if qDNGValidate + + char s [256]; + + sprintf (s, "Unable to repair bad pixel, row %d, column %d", + (int) badPoint.v, + (int) badPoint.h); + + ReportWarning (s); + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::FixSingleColumn (dng_pixel_buffer &buffer, + const dng_rect &badRect) + { + + int32 cs = buffer.fColStep; + + for (int32 row = badRect.t; row < badRect.b; row++) + { + + uint16 *p0 = buffer.DirtyPixel_uint16 (row - 4, badRect.l - 4, 0); + uint16 *p1 = buffer.DirtyPixel_uint16 (row - 3, badRect.l - 4, 0); + uint16 *p2 = buffer.DirtyPixel_uint16 (row - 2, badRect.l - 4, 0); + uint16 *p3 = buffer.DirtyPixel_uint16 (row - 1, badRect.l - 4, 0); + uint16 *p4 = buffer.DirtyPixel_uint16 (row , badRect.l - 4, 0); + uint16 *p5 = buffer.DirtyPixel_uint16 (row + 1, badRect.l - 4, 0); + uint16 *p6 = buffer.DirtyPixel_uint16 (row + 2, badRect.l - 4, 0); + uint16 *p7 = buffer.DirtyPixel_uint16 (row + 3, badRect.l - 4, 0); + uint16 *p8 = buffer.DirtyPixel_uint16 (row + 4, badRect.l - 4, 0); + + uint32 est0; + uint32 est1; + uint32 est2; + uint32 est3; + uint32 est4; + uint32 est5; + uint32 est6; + + uint32 grad0; + uint32 grad1; + uint32 grad2; + uint32 grad3; + uint32 grad4; + uint32 grad5; + uint32 grad6; + + uint32 lower = 0; + uint32 upper = 0x0FFFF; + + if (IsGreen (row, badRect.l)) // Green pixel + { + + // g00 b01 g02 b03 g04 b05 g06 b07 g08 + // r10 g11 r12 g13 r14 g15 r16 g17 r18 + // g20 b21 g22 b23 g24 b25 g26 b27 g28 + // r30 g31 r32 g33 r34 g35 r36 g37 r38 + // g40 b41 g42 b43 g44 b45 g46 b47 g48 + // r50 g51 r52 g53 r54 g55 r56 g57 r58 + // g60 b61 g62 b63 g64 b65 g66 b67 g68 + // r70 g71 r72 g73 r74 g75 r76 g77 r78 + // g80 b81 g82 b83 g84 b85 g86 b87 g88 + + int32 b03 = p0 [3 * cs]; + int32 b05 = p0 [5 * cs]; + + int32 g11 = p1 [1 * cs]; + int32 g13 = p1 [3 * cs]; + int32 g15 = p1 [5 * cs]; + int32 g17 = p1 [7 * cs]; + + int32 g22 = p2 [2 * cs]; + int32 b23 = p2 [3 * cs]; + int32 b25 = p2 [5 * cs]; + int32 g26 = p2 [6 * cs]; + + int32 r30 = p3 [0 * cs]; + int32 g31 = p3 [1 * cs]; + int32 r32 = p3 [2 * cs]; + int32 g33 = p3 [3 * cs]; + int32 g35 = p3 [5 * cs]; + int32 r36 = p3 [6 * cs]; + int32 g37 = p3 [7 * cs]; + int32 r38 = p3 [8 * cs]; + + int32 g40 = p4 [0 * cs]; + int32 g42 = p4 [2 * cs]; + int32 b43 = p4 [3 * cs]; + int32 b45 = p4 [5 * cs]; + int32 g46 = p4 [6 * cs]; + int32 g48 = p4 [8 * cs]; + + int32 r50 = p5 [0 * cs]; + int32 g51 = p5 [1 * cs]; + int32 r52 = p5 [2 * cs]; + int32 g53 = p5 [3 * cs]; + int32 g55 = p5 [5 * cs]; + int32 r56 = p5 [6 * cs]; + int32 g57 = p5 [7 * cs]; + int32 r58 = p5 [8 * cs]; + + int32 g62 = p6 [2 * cs]; + int32 b63 = p6 [3 * cs]; + int32 b65 = p6 [5 * cs]; + int32 g66 = p6 [6 * cs]; + + int32 g71 = p7 [1 * cs]; + int32 g73 = p7 [3 * cs]; + int32 g75 = p7 [5 * cs]; + int32 g77 = p7 [7 * cs]; + + int32 b83 = p8 [3 * cs]; + int32 b85 = p8 [5 * cs]; + + // In case there is some green split, make an estimate of + // of the local difference between the greens, and adjust + // the estimated green values for the difference + // between the two types of green pixels when estimating + // across green types. + + int32 split = ((g22 + g62 + g26 + g66) * 4 + + (g42 + g46 ) * 8 - + (g11 + g13 + g15 + g17) - + (g31 + g33 + g35 + g37) * 3 - + (g51 + g53 + g55 + g57) * 3 - + (g71 + g73 + g75 + g77) + 16) >> 5; + + est0 = g13 + g75 + split * 2; + + grad0 = Abs_int32 (g13 - g75) + + Abs_int32 (g15 - g46) + + Abs_int32 (g22 - g53) + + Abs_int32 (g35 - g66) + + Abs_int32 (g42 - g73) + + Abs_int32 (b03 - b65) + + Abs_int32 (b23 - b85); + + est1 = g33 + g55 + split * 2; + + grad1 = Abs_int32 (g33 - g55) + + Abs_int32 (g22 - g55) + + Abs_int32 (g33 - g66) + + Abs_int32 (g13 - g35) + + Abs_int32 (g53 - g75) + + Abs_int32 (b23 - b45) + + Abs_int32 (b43 - b65); + + est2 = g31 + g57 + split * 2; + + grad2 = Abs_int32 (g31 - g57) + + Abs_int32 (g33 - g46) + + Abs_int32 (g35 - g48) + + Abs_int32 (g40 - g53) + + Abs_int32 (g42 - g55) + + Abs_int32 (r30 - r56) + + Abs_int32 (r32 - r58); + + est3 = g42 + g46; + + grad3 = Abs_int32 (g42 - g46) * 2 + + Abs_int32 (g33 - g35) + + Abs_int32 (g53 - g55) + + Abs_int32 (b23 - b25) + + Abs_int32 (b43 - b45) + + Abs_int32 (b63 - b65); + + est4 = g37 + g51 + split * 2; + + grad4 = Abs_int32 (g37 - g51) + + Abs_int32 (g35 - g42) + + Abs_int32 (g33 - g40) + + Abs_int32 (g48 - g55) + + Abs_int32 (g46 - g53) + + Abs_int32 (r38 - r52) + + Abs_int32 (r36 - r50); + + est5 = g35 + g53 + split * 2; + + grad5 = Abs_int32 (g35 - g53) + + Abs_int32 (g26 - g53) + + Abs_int32 (g35 - g62) + + Abs_int32 (g15 - g33) + + Abs_int32 (g55 - g73) + + Abs_int32 (b25 - b43) + + Abs_int32 (b45 - b63); + + est6 = g15 + g73 + split * 2; + + grad6 = Abs_int32 (g15 - g73) + + Abs_int32 (g13 - g42) + + Abs_int32 (g26 - g55) + + Abs_int32 (g33 - g62) + + Abs_int32 (g46 - g75) + + Abs_int32 (b05 - b63) + + Abs_int32 (b25 - b83); + + lower = Min_uint32 (Min_uint32 (g33, g35), + Min_uint32 (g53, g55)); + + upper = Max_uint32 (Max_uint32 (g33, g35), + Max_uint32 (g53, g55)); + + lower = Pin_int32 (0, lower + split, 65535); + upper = Pin_int32 (0, upper + split, 65535); + + } + + else // Red/blue pixel + { + + // b00 g01 b02 g03 b04 g05 b06 g07 b08 + // g10 r11 g12 r13 g14 r15 g16 r17 g18 + // b20 g21 b22 g23 b24 g25 b26 g27 b28 + // g30 r31 g32 r33 g34 r35 g36 r37 g38 + // b40 g41 b42 g43 b44 g45 b46 g47 b48 + // g50 r51 g52 r53 g54 r55 g56 r57 g58 + // b60 g61 b62 g63 b64 g65 b66 g67 b68 + // g70 r71 g72 r73 g74 r75 g76 r77 g78 + // b80 g81 b82 g83 b84 g85 b86 g87 b88 + + int32 b02 = p0 [2 * cs]; + int32 g03 = p0 [3 * cs]; + int32 g05 = p0 [5 * cs]; + int32 b06 = p0 [6 * cs]; + + int32 r13 = p1 [3 * cs]; + int32 r15 = p1 [5 * cs]; + + int32 b20 = p2 [0 * cs]; + int32 b22 = p2 [2 * cs]; + int32 g23 = p2 [3 * cs]; + int32 g25 = p2 [5 * cs]; + int32 b26 = p2 [6 * cs]; + int32 b28 = p2 [8 * cs]; + + int32 r31 = p3 [1 * cs]; + int32 g32 = p3 [2 * cs]; + int32 r33 = p3 [3 * cs]; + int32 r35 = p3 [5 * cs]; + int32 g36 = p3 [6 * cs]; + int32 r37 = p3 [7 * cs]; + + int32 g41 = p4 [1 * cs]; + int32 b42 = p4 [2 * cs]; + int32 g43 = p4 [3 * cs]; + int32 g45 = p4 [5 * cs]; + int32 b46 = p4 [6 * cs]; + int32 g47 = p4 [7 * cs]; + + int32 r51 = p5 [1 * cs]; + int32 g52 = p5 [2 * cs]; + int32 r53 = p5 [3 * cs]; + int32 r55 = p5 [5 * cs]; + int32 g56 = p5 [6 * cs]; + int32 r57 = p5 [7 * cs]; + + int32 b60 = p6 [0 * cs]; + int32 b62 = p6 [2 * cs]; + int32 g63 = p6 [3 * cs]; + int32 g65 = p6 [5 * cs]; + int32 b66 = p6 [6 * cs]; + int32 b68 = p6 [8 * cs]; + + int32 r73 = p7 [3 * cs]; + int32 r75 = p7 [5 * cs]; + + int32 b82 = p8 [2 * cs]; + int32 g83 = p8 [3 * cs]; + int32 g85 = p8 [5 * cs]; + int32 b86 = p8 [6 * cs]; + + est0 = b02 + b86; + + grad0 = Abs_int32 (b02 - b86) + + Abs_int32 (r13 - r55) + + Abs_int32 (r33 - r75) + + Abs_int32 (g03 - g45) + + Abs_int32 (g23 - g65) + + Abs_int32 (g43 - g85); + + est1 = b22 + b66; + + grad1 = Abs_int32 (b22 - b66) + + Abs_int32 (r13 - r35) + + Abs_int32 (r33 - r55) + + Abs_int32 (r53 - r75) + + Abs_int32 (g23 - g45) + + Abs_int32 (g43 - g65); + + est2 = b20 + b68; + + grad2 = Abs_int32 (b20 - b68) + + Abs_int32 (r31 - r55) + + Abs_int32 (r33 - r57) + + Abs_int32 (g23 - g47) + + Abs_int32 (g32 - g56) + + Abs_int32 (g41 - g65); + + est3 = b42 + b46; + + grad3 = Abs_int32 (b42 - b46) + + Abs_int32 (r33 - r35) + + Abs_int32 (r53 - r55) + + Abs_int32 (g32 - g36) + + Abs_int32 (g43 - g43) + + Abs_int32 (g52 - g56); + + est4 = b28 + b60; + + grad4 = Abs_int32 (b28 - b60) + + Abs_int32 (r37 - r53) + + Abs_int32 (r35 - r51) + + Abs_int32 (g25 - g41) + + Abs_int32 (g36 - g52) + + Abs_int32 (g47 - g63); + + est5 = b26 + b62; + + grad5 = Abs_int32 (b26 - b62) + + Abs_int32 (r15 - r33) + + Abs_int32 (r35 - r53) + + Abs_int32 (r55 - r73) + + Abs_int32 (g25 - g43) + + Abs_int32 (g45 - g63); + + est6 = b06 + b82; + + grad6 = Abs_int32 (b06 - b82) + + Abs_int32 (r15 - r53) + + Abs_int32 (r35 - r73) + + Abs_int32 (g05 - g43) + + Abs_int32 (g25 - g63) + + Abs_int32 (g45 - g83); + + lower = Min_uint32 (b42, b46); + upper = Max_uint32 (b42, b46); + + } + + uint32 minGrad = Min_uint32 (grad0, grad1); + + minGrad = Min_uint32 (minGrad, grad2); + minGrad = Min_uint32 (minGrad, grad3); + minGrad = Min_uint32 (minGrad, grad4); + minGrad = Min_uint32 (minGrad, grad5); + minGrad = Min_uint32 (minGrad, grad6); + + uint32 limit = (minGrad * 3) >> 1; + + uint32 total = 0; + uint32 count = 0; + + if (grad0 <= limit) + { + total += est0; + count += 2; + } + + if (grad1 <= limit) + { + total += est1; + count += 2; + } + + if (grad2 <= limit) + { + total += est2; + count += 2; + } + + if (grad3 <= limit) + { + total += est3; + count += 2; + } + + if (grad4 <= limit) + { + total += est4; + count += 2; + } + + if (grad5 <= limit) + { + total += est5; + count += 2; + } + + if (grad6 <= limit) + { + total += est6; + count += 2; + } + + uint32 estimate = (total + (count >> 1)) / count; + + p4 [4] = (uint16) Pin_uint32 (lower, estimate, upper); + + } + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::FixSingleRow (dng_pixel_buffer &buffer, + const dng_rect &badRect) + { + + dng_pixel_buffer tBuffer = buffer; + + tBuffer.fArea = Transpose (buffer.fArea); + + tBuffer.fRowStep = buffer.fColStep; + tBuffer.fColStep = buffer.fRowStep; + + dng_rect tBadRect = Transpose (badRect); + + FixSingleColumn (tBuffer, tBadRect); + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::FixClusteredRect (dng_pixel_buffer &buffer, + const dng_rect &badRect, + const dng_rect &imageBounds) + { + + const uint32 kNumSets = 8; + const uint32 kSetSize = 8; + + static const int32 kOffset [kNumSets] [kSetSize] [2] = + { + { + { -1, 1 }, + { -1, -1 }, + { 1, -1 }, + { 1, 1 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + }, + { + { -2, 0 }, + { 2, 0 }, + { 0, -2 }, + { 0, 2 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + }, + { + { -2, -2 }, + { -2, 2 }, + { 2, -2 }, + { 2, 2 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + }, + { + { -1, -3 }, + { -3, -1 }, + { 1, -3 }, + { 3, -1 }, + { -1, 3 }, + { -3, 1 }, + { 1, 3 }, + { 3, 1 } + }, + { + { -4, 0 }, + { 4, 0 }, + { 0, -4 }, + { 0, 4 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + }, + { + { -3, -3 }, + { -3, 3 }, + { 3, -3 }, + { 3, 3 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + }, + { + { -2, -4 }, + { -4, -2 }, + { 2, -4 }, + { 4, -2 }, + { -2, 4 }, + { -4, 2 }, + { 2, 4 }, + { 4, 2 } + }, + { + { -4, -4 }, + { -4, 4 }, + { 4, -4 }, + { 4, 4 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 }, + { 0, 0 } + } + }; + + bool didFail = false; + + for (int32 row = badRect.t; row < badRect.b; row++) + { + + for (int32 col = badRect.l; col < badRect.r; col++) + { + + uint16 *p = buffer.DirtyPixel_uint16 (row, col, 0); + + bool isGreen = IsGreen (row, col); + + bool didFixPixel = false; + + for (uint32 set = 0; set < kNumSets && !didFixPixel; set++) + { + + if (!isGreen && (kOffset [set] [0] [0] & 1) == 1) + { + continue; + } + + uint32 total = 0; + uint32 count = 0; + + for (uint32 entry = 0; entry < kSetSize; entry++) + { + + dng_point offset (kOffset [set] [entry] [0], + kOffset [set] [entry] [1]); + + if (offset.v == 0 && + offset.h == 0) + { + break; + } + + if (fList->IsPointValid (dng_point (row, col) + offset, + imageBounds)) + { + + total += p [offset.v * buffer.fRowStep + + offset.h * buffer.fColStep]; + + count++; + + } + + } + + if (count) + { + + uint32 estimate = (total + (count >> 1)) / count; + + p [0] = (uint16) estimate; + + didFixPixel = true; + + } + + } + + if (!didFixPixel) + { + + didFail = true; + + } + + } + + } + + #if qDNGValidate + + if (didFail) + { + + ReportWarning ("Unable to repair bad rectangle"); + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_FixBadPixelsList::ProcessArea (dng_negative & /* negative */, + uint32 /* threadIndex */, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer, + const dng_rect &dstArea, + const dng_rect &imageBounds) + { + + uint32 pointCount = fList->PointCount (); + uint32 rectCount = fList->RectCount (); + + dng_rect fixArea = dstArea; + + if (rectCount) + { + fixArea.t -= kBadRectPadding; + fixArea.l -= kBadRectPadding; + fixArea.b += kBadRectPadding; + fixArea.r += kBadRectPadding; + } + + bool didFixPoint = false; + + if (pointCount) + { + + for (uint32 pointIndex = 0; pointIndex < pointCount; pointIndex++) + { + + dng_point badPoint = fList->Point (pointIndex); + + if (badPoint.v >= fixArea.t && + badPoint.h >= fixArea.l && + badPoint.v < fixArea.b && + badPoint.h < fixArea.r) + { + + bool isIsolated = fList->IsPointIsolated (pointIndex, + kBadPointPadding); + + if (isIsolated && + badPoint.v >= imageBounds.t + kBadPointPadding && + badPoint.h >= imageBounds.l + kBadPointPadding && + badPoint.v < imageBounds.b - kBadPointPadding && + badPoint.h < imageBounds.r - kBadPointPadding) + { + + FixIsolatedPixel (srcBuffer, + badPoint); + + } + + else + { + + FixClusteredPixel (srcBuffer, + pointIndex, + imageBounds); + + } + + didFixPoint = true; + + } + + } + + } + + if (rectCount) + { + + if (didFixPoint) + { + + srcBuffer.RepeatSubArea (imageBounds, + SrcRepeat ().v, + SrcRepeat ().h); + + } + + for (uint32 rectIndex = 0; rectIndex < rectCount; rectIndex++) + { + + dng_rect badRect = fList->Rect (rectIndex); + + dng_rect overlap = dstArea & badRect; + + if (overlap.NotEmpty ()) + { + + bool isIsolated = fList->IsRectIsolated (rectIndex, + kBadRectPadding); + + if (isIsolated && + badRect.r == badRect.l + 1 && + badRect.l >= imageBounds.l + SrcRepeat ().h && + badRect.r <= imageBounds.r - SrcRepeat ().v) + { + + FixSingleColumn (srcBuffer, + overlap); + + } + + else if (isIsolated && + badRect.b == badRect.t + 1 && + badRect.t >= imageBounds.t + SrcRepeat ().h && + badRect.b <= imageBounds.b - SrcRepeat ().v) + { + + FixSingleRow (srcBuffer, + overlap); + + } + + else + { + + FixClusteredRect (srcBuffer, + overlap, + imageBounds); + + } + + } + + } + + } + + dstBuffer.CopyArea (srcBuffer, + dstArea, + 0, + dstBuffer.fPlanes); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_bad_pixels.h b/source/lib/dng_sdk/dng_bad_pixels.h new file mode 100644 index 0000000..c7b65d6 --- /dev/null +++ b/source/lib/dng_sdk/dng_bad_pixels.h @@ -0,0 +1,307 @@ +/*****************************************************************************/ +// Copyright 2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_bad_pixels.h#3 $ */ +/* $DateTime: 2012/07/11 10:36:56 $ */ +/* $Change: 838485 $ */ +/* $Author: tknoll $ */ + +/** \file + * Opcodes to fix defective pixels, including individual pixels and regions (such as + * defective rows and columns). + */ + +/*****************************************************************************/ + +#ifndef __dng_bad_pixels__ +#define __dng_bad_pixels__ + +/*****************************************************************************/ + +#include "dng_opcodes.h" + +#include + +/*****************************************************************************/ + +/// \brief An opcode to fix individual bad pixels that are marked with a constant +/// value (e.g., 0) in a Bayer image. + +class dng_opcode_FixBadPixelsConstant: public dng_filter_opcode + { + + private: + + uint32 fConstant; + + uint32 fBayerPhase; + + public: + + /// Construct an opcode to fix an individual bad pixels that are marked with + /// a constant value in a Bayer image. + /// \param constant The constant value that indicates a bad pixel. + /// \param bayerPhase The phase of the Bayer mosaic pattern (0, 1, 2, 3). + + dng_opcode_FixBadPixelsConstant (uint32 constant, + uint32 bayerPhase); + + dng_opcode_FixBadPixelsConstant (dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual dng_point SrcRepeat (); + + virtual dng_rect SrcArea (const dng_rect &dstArea, + const dng_rect &imageBounds); + + virtual void Prepare (dng_negative &negative, + uint32 threadCount, + const dng_point &tileSize, + const dng_rect &imageBounds, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator &allocator); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + protected: + + bool IsGreen (int32 row, int32 col) const + { + return (((uint32) row + (uint32) col + fBayerPhase + (fBayerPhase >> 1)) & 1) == 0; + } + + }; + +/*****************************************************************************/ + +/// \brief A list of bad pixels and rectangles (usually single rows or columns). + +class dng_bad_pixel_list + { + + public: + + enum + { + kNoIndex = 0xFFFFFFFF + }; + + private: + + // List of bad single pixels. + + std::vector fBadPoints; + + // List of bad rectangles (usually single rows or columns). + + std::vector fBadRects; + + public: + + /// Create an empty bad pixel list. + + dng_bad_pixel_list (); + + /// Returns the number of bad single pixels. + + uint32 PointCount () const + { + return (uint32) fBadPoints.size (); + } + + /// Retrieves the bad single pixel coordinate via the specified list index. + /// + /// \param index The list index from which to retrieve the bad single pixel + /// coordinate. + + const dng_point & Point (uint32 index) const + { + return fBadPoints [index]; + } + + /// Returns the number of bad rectangles. + + uint32 RectCount () const + { + return (uint32) fBadRects.size (); + } + + /// Retrieves the bad rectangle via the specified list index. + /// + /// \param index The list index from which to retrieve the bad rectangle + /// coordinates. + + const dng_rect & Rect (uint32 index) const + { + return fBadRects [index]; + } + + /// Returns true iff there are zero bad single pixels and zero bad + /// rectangles. + + bool IsEmpty () const + { + return PointCount () == 0 && + RectCount () == 0; + } + + /// Returns true iff there is at least one bad single pixel or at least one + /// bad rectangle. + + bool NotEmpty () const + { + return !IsEmpty (); + } + + /// Add the specified coordinate to the list of bad single pixels. + /// + /// \param pt The bad single pixel to add. + + void AddPoint (const dng_point &pt); + + /// Add the specified rectangle to the list of bad rectangles. + /// + /// \param pt The bad rectangle to add. + + void AddRect (const dng_rect &r); + + /// Sort the bad single pixels and bad rectangles by coordinates (top to + /// bottom, then left to right). + + void Sort (); + + /// Returns true iff the specified bad single pixel is isolated, i.e., there + /// is no other bad single pixel or bad rectangle that lies within radius + /// pixels of this bad single pixel. + /// + /// \param index The index of the bad single pixel to test. + /// \param radius The pixel radius to test for isolation. + + bool IsPointIsolated (uint32 index, + uint32 radius) const; + + /// Returns true iff the specified bad rectangle is isolated, i.e., there + /// is no other bad single pixel or bad rectangle that lies within radius + /// pixels of this bad rectangle. + /// + /// \param index The index of the bad rectangle to test. + /// \param radius The pixel radius to test for isolation. + + bool IsRectIsolated (uint32 index, + uint32 radius) const; + + /// Returns true iff the specified point is valid, i.e., lies within the + /// specified image bounds, is different from all other bad single pixels, + /// and is not contained in any bad rectangle. The second and third + /// conditions are only checked if provided with a starting search index. + /// + /// \param pt The point to test for validity. + /// \param imageBounds The pt must lie within imageBounds to be valid. + /// \index The search index to use (or kNoIndex, to avoid a search) for + /// checking for validity. + + bool IsPointValid (const dng_point &pt, + const dng_rect &imageBounds, + uint32 index = kNoIndex) const; + + }; + +/*****************************************************************************/ + +/// \brief An opcode to fix lists of bad pixels (indicated by position) in a Bayer +/// image. + +class dng_opcode_FixBadPixelsList: public dng_filter_opcode + { + + protected: + + enum + { + kBadPointPadding = 2, + kBadRectPadding = 4 + }; + + private: + + AutoPtr fList; + + uint32 fBayerPhase; + + public: + + /// Construct an opcode to fix lists of bad pixels (indicated by position) in + /// a Bayer image. + /// \param list The list of bad pixels to fix. + /// \param bayerPhase The phase of the Bayer mosaic pattern (0, 1, 2, 3). + + dng_opcode_FixBadPixelsList (AutoPtr &list, + uint32 bayerPhase); + + dng_opcode_FixBadPixelsList (dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual dng_point SrcRepeat (); + + virtual dng_rect SrcArea (const dng_rect &dstArea, + const dng_rect &imageBounds); + + virtual void Prepare (dng_negative &negative, + uint32 threadCount, + const dng_point &tileSize, + const dng_rect &imageBounds, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator &allocator); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + protected: + + bool IsGreen (int32 row, int32 col) const + { + return ((row + col + fBayerPhase + (fBayerPhase >> 1)) & 1) == 0; + } + + virtual void FixIsolatedPixel (dng_pixel_buffer &buffer, + dng_point &badPoint); + + virtual void FixClusteredPixel (dng_pixel_buffer &buffer, + uint32 pointIndex, + const dng_rect &imageBounds); + + virtual void FixSingleColumn (dng_pixel_buffer &buffer, + const dng_rect &badRect); + + virtual void FixSingleRow (dng_pixel_buffer &buffer, + const dng_rect &badRect); + + virtual void FixClusteredRect (dng_pixel_buffer &buffer, + const dng_rect &badRect, + const dng_rect &imageBounds); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_bottlenecks.cpp b/source/lib/dng_sdk/dng_bottlenecks.cpp new file mode 100644 index 0000000..44fdb7a --- /dev/null +++ b/source/lib/dng_sdk/dng_bottlenecks.cpp @@ -0,0 +1,72 @@ +/*****************************************************************************/ +// Copyright 2006-2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_bottlenecks.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_bottlenecks.h" + +#include "dng_reference.h" + +/*****************************************************************************/ + +dng_suite gDNGSuite = + { + RefZeroBytes, + RefCopyBytes, + RefSwapBytes16, + RefSwapBytes32, + RefSetArea8, + RefSetArea16, + RefSetArea32, + RefCopyArea8, + RefCopyArea16, + RefCopyArea32, + RefCopyArea8_16, + RefCopyArea8_S16, + RefCopyArea8_32, + RefCopyArea16_S16, + RefCopyArea16_32, + RefCopyArea8_R32, + RefCopyArea16_R32, + RefCopyAreaS16_R32, + RefCopyAreaR32_8, + RefCopyAreaR32_16, + RefCopyAreaR32_S16, + RefRepeatArea8, + RefRepeatArea16, + RefRepeatArea32, + RefShiftRight16, + RefBilinearRow16, + RefBilinearRow32, + RefBaselineABCtoRGB, + RefBaselineABCDtoRGB, + RefBaselineHueSatMap, + RefBaselineRGBtoGray, + RefBaselineRGBtoRGB, + RefBaseline1DTable, + RefBaselineRGBTone, + RefResampleDown16, + RefResampleDown32, + RefResampleAcross16, + RefResampleAcross32, + RefEqualBytes, + RefEqualArea8, + RefEqualArea16, + RefEqualArea32, + RefVignetteMask16, + RefVignette16, + RefVignette32, + RefMapArea16 + }; + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_bottlenecks.h b/source/lib/dng_sdk/dng_bottlenecks.h new file mode 100644 index 0000000..ff8cbb0 --- /dev/null +++ b/source/lib/dng_sdk/dng_bottlenecks.h @@ -0,0 +1,1715 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_bottlenecks.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Indirection mechanism for performance-critical routines that might be replaced + * with hand-optimized or hardware-specific implementations. + */ + +/*****************************************************************************/ + +#ifndef __dng_bottlenecks__ +#define __dng_bottlenecks__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +typedef void (ZeroBytesProc) + (void *dPtr, + uint32 count); + +typedef void (CopyBytesProc) + (const void *sPtr, + void *dPtr, + uint32 count); + +/*****************************************************************************/ + +typedef void (SwapBytes16Proc) + (uint16 *dPtr, + uint32 count); + +typedef void (SwapBytes32Proc) + (uint32 *dPtr, + uint32 count); + +/*****************************************************************************/ + +typedef void (SetArea8Proc) + (uint8 *dPtr, + uint8 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); + +typedef void (SetArea16Proc) + (uint16 *dPtr, + uint16 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); + +typedef void (SetArea32Proc) + (uint32 *dPtr, + uint32 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); + +/*****************************************************************************/ + +typedef void (CopyArea8Proc) + (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea16Proc) + (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea32Proc) + (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea8_16Proc) + (const uint8 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea8_S16Proc) + (const uint8 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea8_32Proc) + (const uint8 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea16_S16Proc) + (const uint16 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea16_32Proc) + (const uint16 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef void (CopyArea8_R32Proc) + (const uint8 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +typedef void (CopyArea16_R32Proc) + (const uint16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +typedef void (CopyAreaS16_R32Proc) + (const int16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +typedef void (CopyAreaR32_8Proc) + (const real32 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +typedef void (CopyAreaR32_16Proc) + (const real32 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +typedef void (CopyAreaR32_S16Proc) + (const real32 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +/*****************************************************************************/ + +typedef void (RepeatArea8Proc) + (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH); + +typedef void (RepeatArea16Proc) + (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH); + +typedef void (RepeatArea32Proc) + (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH); + +/*****************************************************************************/ + +typedef void (ShiftRight16Proc) + (uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 shift); + +/*****************************************************************************/ + +typedef void (BilinearRow16Proc) + (const uint16 *sPtr, + uint16 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const uint16 * const * kernWeights, + uint32 sShift); + +typedef void (BilinearRow32Proc) + (const real32 *sPtr, + real32 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const real32 * const * kernWeights, + uint32 sShift); + +/*****************************************************************************/ + +typedef void (BaselineABCtoRGBProc) + (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB); + +typedef void (BaselineABCDtoRGBProc) + (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + const real32 *sPtrD, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB); + +/*****************************************************************************/ + +typedef void (BaselineHueSatMapProc) + (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_hue_sat_map &lut, + const dng_1d_table *encodeTable, + const dng_1d_table *decodeTable); + +/*****************************************************************************/ + +typedef void (BaselineGrayToRGBProc) + (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrG, + uint32 count, + const dng_matrix &matrix); + +typedef void (BaselineRGBtoRGBProc) + (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_matrix &matrix); + +/*****************************************************************************/ + +typedef void (Baseline1DTableProc) + (const real32 *sPtr, + real32 *dPtr, + uint32 count, + const dng_1d_table &table); + +/*****************************************************************************/ + +typedef void (BaselineRGBToneProc) + (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_1d_table &table); + +/*****************************************************************************/ + +typedef void (ResampleDown16Proc) + (const uint16 *sPtr, + uint16 *dPtr, + uint32 sCount, + int32 sRowStep, + const int16 *wPtr, + uint32 wCount, + uint32 pixelRange); + +typedef void (ResampleDown32Proc) + (const real32 *sPtr, + real32 *dPtr, + uint32 sCount, + int32 sRowStep, + const real32 *wPtr, + uint32 wCount); + +/*****************************************************************************/ + +typedef void (ResampleAcross16Proc) + (const uint16 *sPtr, + uint16 *dPtr, + uint32 dCount, + const int32 *coord, + const int16 *wPtr, + uint32 wCount, + uint32 wStep, + uint32 pixelRange); + +typedef void (ResampleAcross32Proc) + (const real32 *sPtr, + real32 *dPtr, + uint32 dCount, + const int32 *coord, + const real32 *wPtr, + uint32 wCount, + uint32 wStep); + +/*****************************************************************************/ + +typedef bool (EqualBytesProc) + (const void *sPtr, + const void *dPtr, + uint32 count); + +typedef bool (EqualArea8Proc) + (const uint8 *sPtr, + const uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef bool (EqualArea16Proc) + (const uint16 *sPtr, + const uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +typedef bool (EqualArea32Proc) + (const uint32 *sPtr, + const uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +/*****************************************************************************/ + +typedef void (VignetteMask16Proc) + (uint16 *mPtr, + uint32 rows, + uint32 cols, + int32 rowStep, + int64 offsetH, + int64 offsetV, + int64 stepH, + int64 stepV, + uint32 tBits, + const uint16 *table); + +typedef void (Vignette16Proc) + (int16 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits); + +/*****************************************************************************/ + +typedef void (Vignette32Proc) + (real32 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits); + +/*****************************************************************************/ + +typedef void (MapArea16Proc) + (uint16 *dPtr, + uint32 count0, + uint32 count1, + uint32 count2, + int32 step0, + int32 step1, + int32 step2, + const uint16 *map); + +/*****************************************************************************/ + +struct dng_suite + { + ZeroBytesProc *ZeroBytes; + CopyBytesProc *CopyBytes; + SwapBytes16Proc *SwapBytes16; + SwapBytes32Proc *SwapBytes32; + SetArea8Proc *SetArea8; + SetArea16Proc *SetArea16; + SetArea32Proc *SetArea32; + CopyArea8Proc *CopyArea8; + CopyArea16Proc *CopyArea16; + CopyArea32Proc *CopyArea32; + CopyArea8_16Proc *CopyArea8_16; + CopyArea8_S16Proc *CopyArea8_S16; + CopyArea8_32Proc *CopyArea8_32; + CopyArea16_S16Proc *CopyArea16_S16; + CopyArea16_32Proc *CopyArea16_32; + CopyArea8_R32Proc *CopyArea8_R32; + CopyArea16_R32Proc *CopyArea16_R32; + CopyAreaS16_R32Proc *CopyAreaS16_R32; + CopyAreaR32_8Proc *CopyAreaR32_8; + CopyAreaR32_16Proc *CopyAreaR32_16; + CopyAreaR32_S16Proc *CopyAreaR32_S16; + RepeatArea8Proc *RepeatArea8; + RepeatArea16Proc *RepeatArea16; + RepeatArea32Proc *RepeatArea32; + ShiftRight16Proc *ShiftRight16; + BilinearRow16Proc *BilinearRow16; + BilinearRow32Proc *BilinearRow32; + BaselineABCtoRGBProc *BaselineABCtoRGB; + BaselineABCDtoRGBProc *BaselineABCDtoRGB; + BaselineHueSatMapProc *BaselineHueSatMap; + BaselineGrayToRGBProc *BaselineRGBtoGray; + BaselineRGBtoRGBProc *BaselineRGBtoRGB; + Baseline1DTableProc *Baseline1DTable; + BaselineRGBToneProc *BaselineRGBTone; + ResampleDown16Proc *ResampleDown16; + ResampleDown32Proc *ResampleDown32; + ResampleAcross16Proc *ResampleAcross16; + ResampleAcross32Proc *ResampleAcross32; + EqualBytesProc *EqualBytes; + EqualArea8Proc *EqualArea8; + EqualArea16Proc *EqualArea16; + EqualArea32Proc *EqualArea32; + VignetteMask16Proc *VignetteMask16; + Vignette16Proc *Vignette16; + Vignette32Proc *Vignette32; + MapArea16Proc *MapArea16; + }; + +/*****************************************************************************/ + +extern dng_suite gDNGSuite; + +/*****************************************************************************/ + +inline void DoZeroBytes (void *dPtr, + uint32 count) + { + + (gDNGSuite.ZeroBytes) (dPtr, + count); + + } + +inline void DoCopyBytes (const void *sPtr, + void *dPtr, + uint32 count) + { + + (gDNGSuite.CopyBytes) (sPtr, + dPtr, + count); + + } + +/*****************************************************************************/ + +inline void DoSwapBytes16 (uint16 *dPtr, + uint32 count) + { + + (gDNGSuite.SwapBytes16) (dPtr, + count); + + } + +inline void DoSwapBytes32 (uint32 *dPtr, + uint32 count) + { + + (gDNGSuite.SwapBytes32) (dPtr, + count); + + } + +/*****************************************************************************/ + +inline void DoSetArea8 (uint8 *dPtr, + uint8 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep) + { + + (gDNGSuite.SetArea8) (dPtr, + value, + rows, + cols, + planes, + rowStep, + colStep, + planeStep); + + } + +inline void DoSetArea16 (uint16 *dPtr, + uint16 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep) + { + + (gDNGSuite.SetArea16) (dPtr, + value, + rows, + cols, + planes, + rowStep, + colStep, + planeStep); + + } + +inline void DoSetArea32 (uint32 *dPtr, + uint32 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep) + { + + (gDNGSuite.SetArea32) (dPtr, + value, + rows, + cols, + planes, + rowStep, + colStep, + planeStep); + + } + +/*****************************************************************************/ + +inline void DoCopyArea8 (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea8) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea32 (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea8_16 (const uint8 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea8_16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea8_S16 (const uint8 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea8_S16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea8_32 (const uint8 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea8_32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea16_S16 (const uint16 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea16_S16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea16_32 (const uint16 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + (gDNGSuite.CopyArea16_32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline void DoCopyArea8_R32 (const uint8 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + (gDNGSuite.CopyArea8_R32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + pixelRange); + + } + +inline void DoCopyArea16_R32 (const uint16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + (gDNGSuite.CopyArea16_R32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + pixelRange); + + } + +inline void DoCopyAreaS16_R32 (const int16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + (gDNGSuite.CopyAreaS16_R32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + pixelRange); + + } + +inline void DoCopyAreaR32_8 (const real32 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + (gDNGSuite.CopyAreaR32_8) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + pixelRange); + + } + +inline void DoCopyAreaR32_16 (const real32 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + (gDNGSuite.CopyAreaR32_16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + pixelRange); + + } + +inline void DoCopyAreaR32_S16 (const real32 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + (gDNGSuite.CopyAreaR32_S16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + pixelRange); + + } + +/*****************************************************************************/ + +inline void DoRepeatArea8 (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH) + { + + (gDNGSuite.RepeatArea8) (sPtr, + dPtr, + rows, + cols, + planes, + rowStep, + colStep, + planeStep, + repeatV, + repeatH, + phaseV, + phaseH); + + } + +inline void DoRepeatArea16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH) + { + + (gDNGSuite.RepeatArea16) (sPtr, + dPtr, + rows, + cols, + planes, + rowStep, + colStep, + planeStep, + repeatV, + repeatH, + phaseV, + phaseH); + + } + +inline void DoRepeatArea32 (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH) + { + + (gDNGSuite.RepeatArea32) (sPtr, + dPtr, + rows, + cols, + planes, + rowStep, + colStep, + planeStep, + repeatV, + repeatH, + phaseV, + phaseH); + + } + +/*****************************************************************************/ + +inline void DoShiftRight16 (uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 shift) + { + + (gDNGSuite.ShiftRight16) (dPtr, + rows, + cols, + planes, + rowStep, + colStep, + planeStep, + shift); + + } + +/*****************************************************************************/ + +inline void DoBilinearRow16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const uint16 * const * kernWeights, + uint32 sShift) + { + + (gDNGSuite.BilinearRow16) (sPtr, + dPtr, + cols, + patPhase, + patCount, + kernCounts, + kernOffsets, + kernWeights, + sShift); + + } + +inline void DoBilinearRow32 (const real32 *sPtr, + real32 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const real32 * const * kernWeights, + uint32 sShift) + { + + (gDNGSuite.BilinearRow32) (sPtr, + dPtr, + cols, + patPhase, + patCount, + kernCounts, + kernOffsets, + kernWeights, + sShift); + + } + +/*****************************************************************************/ + +inline void DoBaselineABCtoRGB (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB) + { + + (gDNGSuite.BaselineABCtoRGB) (sPtrA, + sPtrB, + sPtrC, + dPtrR, + dPtrG, + dPtrB, + count, + cameraWhite, + cameraToRGB); + + } + +inline void DoBaselineABCDtoRGB (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + const real32 *sPtrD, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB) + { + + (gDNGSuite.BaselineABCDtoRGB) (sPtrA, + sPtrB, + sPtrC, + sPtrD, + dPtrR, + dPtrG, + dPtrB, + count, + cameraWhite, + cameraToRGB); + + } + +/*****************************************************************************/ + +inline void DoBaselineHueSatMap (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_hue_sat_map &lut, + const dng_1d_table *encodeTable, + const dng_1d_table *decodeTable) + { + + (gDNGSuite.BaselineHueSatMap) (sPtrR, + sPtrG, + sPtrB, + dPtrR, + dPtrG, + dPtrB, + count, + lut, + encodeTable, + decodeTable); + + } + +/*****************************************************************************/ + +inline void DoBaselineRGBtoGray (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrG, + uint32 count, + const dng_matrix &matrix) + { + + (gDNGSuite.BaselineRGBtoGray) (sPtrR, + sPtrG, + sPtrB, + dPtrG, + count, + matrix); + + } + +inline void DoBaselineRGBtoRGB (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_matrix &matrix) + { + + (gDNGSuite.BaselineRGBtoRGB) (sPtrR, + sPtrG, + sPtrB, + dPtrR, + dPtrG, + dPtrB, + count, + matrix); + + } + +/*****************************************************************************/ + +inline void DoBaseline1DTable (const real32 *sPtr, + real32 *dPtr, + uint32 count, + const dng_1d_table &table) + { + + (gDNGSuite.Baseline1DTable) (sPtr, + dPtr, + count, + table); + + } + +/*****************************************************************************/ + +inline void DoBaselineRGBTone (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_1d_table &table) + { + + (gDNGSuite.BaselineRGBTone) (sPtrR, + sPtrG, + sPtrB, + dPtrR, + dPtrG, + dPtrB, + count, + table); + + } + +/*****************************************************************************/ + +inline void DoResampleDown16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 sCount, + int32 sRowStep, + const int16 *wPtr, + uint32 wCount, + uint32 pixelRange) + { + + (gDNGSuite.ResampleDown16) (sPtr, + dPtr, + sCount, + sRowStep, + wPtr, + wCount, + pixelRange); + + } + +inline void DoResampleDown32 (const real32 *sPtr, + real32 *dPtr, + uint32 sCount, + int32 sRowStep, + const real32 *wPtr, + uint32 wCount) + { + + (gDNGSuite.ResampleDown32) (sPtr, + dPtr, + sCount, + sRowStep, + wPtr, + wCount); + + } + +/*****************************************************************************/ + +inline void DoResampleAcross16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 dCount, + const int32 *coord, + const int16 *wPtr, + uint32 wCount, + uint32 wStep, + uint32 pixelRange) + { + + (gDNGSuite.ResampleAcross16) (sPtr, + dPtr, + dCount, + coord, + wPtr, + wCount, + wStep, + pixelRange); + + } + +inline void DoResampleAcross32 (const real32 *sPtr, + real32 *dPtr, + uint32 dCount, + const int32 *coord, + const real32 *wPtr, + uint32 wCount, + uint32 wStep) + { + + (gDNGSuite.ResampleAcross32) (sPtr, + dPtr, + dCount, + coord, + wPtr, + wCount, + wStep); + + } + +/*****************************************************************************/ + +inline bool DoEqualBytes (const void *sPtr, + const void *dPtr, + uint32 count) + { + + return (gDNGSuite.EqualBytes) (sPtr, + dPtr, + count); + + } + +inline bool DoEqualArea8 (const uint8 *sPtr, + const uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + return (gDNGSuite.EqualArea8) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline bool DoEqualArea16 (const uint16 *sPtr, + const uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + return (gDNGSuite.EqualArea16) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +inline bool DoEqualArea32 (const uint32 *sPtr, + const uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + return (gDNGSuite.EqualArea32) (sPtr, + dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + } + +/*****************************************************************************/ + +inline void DoVignetteMask16 (uint16 *mPtr, + uint32 rows, + uint32 cols, + int32 rowStep, + int64 offsetH, + int64 offsetV, + int64 stepH, + int64 stepV, + uint32 tBits, + const uint16 *table) + { + + (gDNGSuite.VignetteMask16) (mPtr, + rows, + cols, + rowStep, + offsetH, + offsetV, + stepH, + stepV, + tBits, + table); + + } + +/*****************************************************************************/ + +inline void DoVignette16 (int16 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits) + { + + (gDNGSuite.Vignette16) (sPtr, + mPtr, + rows, + cols, + planes, + sRowStep, + sPlaneStep, + mRowStep, + mBits); + + } + +/*****************************************************************************/ + +inline void DoVignette32 (real32 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits) + { + + (gDNGSuite.Vignette32) (sPtr, + mPtr, + rows, + cols, + planes, + sRowStep, + sPlaneStep, + mRowStep, + mBits); + + } + +/*****************************************************************************/ + +inline void DoMapArea16 (uint16 *dPtr, + uint32 count0, + uint32 count1, + uint32 count2, + int32 step0, + int32 step1, + int32 step2, + const uint16 *map) + { + + (gDNGSuite.MapArea16) (dPtr, + count0, + count1, + count2, + step0, + step1, + step2, + map); + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_camera_profile.cpp b/source/lib/dng_sdk/dng_camera_profile.cpp new file mode 100644 index 0000000..6983b6d --- /dev/null +++ b/source/lib/dng_sdk/dng_camera_profile.cpp @@ -0,0 +1,1381 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_camera_profile.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +#include "dng_camera_profile.h" + +#include "dng_1d_table.h" +#include "dng_assertions.h" +#include "dng_color_space.h" +#include "dng_host.h" +#include "dng_exceptions.h" +#include "dng_image_writer.h" +#include "dng_info.h" +#include "dng_parse_utils.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_temperature.h" +#include "dng_xy_coord.h" + +/*****************************************************************************/ + +const char * kProfileName_Embedded = "Embedded"; + +const char * kAdobeCalibrationSignature = "com.adobe"; + +/*****************************************************************************/ + +dng_camera_profile::dng_camera_profile () + + : fName () + , fCalibrationIlluminant1 (lsUnknown) + , fCalibrationIlluminant2 (lsUnknown) + , fColorMatrix1 () + , fColorMatrix2 () + , fForwardMatrix1 () + , fForwardMatrix2 () + , fReductionMatrix1 () + , fReductionMatrix2 () + , fFingerprint () + , fCopyright () + , fEmbedPolicy (pepAllowCopying) + , fHueSatDeltas1 () + , fHueSatDeltas2 () + , fHueSatMapEncoding (encoding_Linear) + , fLookTable () + , fLookTableEncoding (encoding_Linear) + , fBaselineExposureOffset (0, 100) + , fDefaultBlackRender (defaultBlackRender_Auto) + , fToneCurve () + , fProfileCalibrationSignature () + , fUniqueCameraModelRestriction () + , fWasReadFromDNG (false) + , fWasReadFromDisk (false) + , fWasBuiltinMatrix (false) + , fWasStubbed (false) + + { + + fToneCurve.SetInvalid (); + + } + +/*****************************************************************************/ + +dng_camera_profile::~dng_camera_profile () + { + + } + +/*****************************************************************************/ + +real64 dng_camera_profile::IlluminantToTemperature (uint32 light) + { + + switch (light) + { + + case lsStandardLightA: + case lsTungsten: + { + return 2850.0; + } + + case lsISOStudioTungsten: + { + return 3200.0; + } + + case lsD50: + { + return 5000.0; + } + + case lsD55: + case lsDaylight: + case lsFineWeather: + case lsFlash: + case lsStandardLightB: + { + return 5500.0; + } + + case lsD65: + case lsStandardLightC: + case lsCloudyWeather: + { + return 6500.0; + } + + case lsD75: + case lsShade: + { + return 7500.0; + } + + case lsDaylightFluorescent: + { + return (5700.0 + 7100.0) * 0.5; + } + + case lsDayWhiteFluorescent: + { + return (4600.0 + 5500.0) * 0.5; + } + + case lsCoolWhiteFluorescent: + case lsFluorescent: + { + return (3800.0 + 4500.0) * 0.5; + } + + case lsWhiteFluorescent: + { + return (3250.0 + 3800.0) * 0.5; + } + + case lsWarmWhiteFluorescent: + { + return (2600.0 + 3250.0) * 0.5; + } + + default: + { + return 0.0; + } + + } + + } + +/******************************************************************************/ + +void dng_camera_profile::NormalizeColorMatrix (dng_matrix &m) + { + + if (m.NotEmpty ()) + { + + // Find scale factor to normalize the matrix. + + dng_vector coord = m * PCStoXYZ (); + + real64 maxCoord = coord.MaxEntry (); + + if (maxCoord > 0.0 && (maxCoord < 0.99 || maxCoord > 1.01)) + { + + m.Scale (1.0 / maxCoord); + + } + + // Round to four decimal places. + + m.Round (10000); + + } + + } + +/******************************************************************************/ + +void dng_camera_profile::SetColorMatrix1 (const dng_matrix &m) + { + + fColorMatrix1 = m; + + NormalizeColorMatrix (fColorMatrix1); + + ClearFingerprint (); + + } + +/******************************************************************************/ + +void dng_camera_profile::SetColorMatrix2 (const dng_matrix &m) + { + + fColorMatrix2 = m; + + NormalizeColorMatrix (fColorMatrix2); + + ClearFingerprint (); + + } + +/******************************************************************************/ + +// Make sure the forward matrix maps to exactly the PCS. + +void dng_camera_profile::NormalizeForwardMatrix (dng_matrix &m) + { + + if (m.NotEmpty ()) + { + + dng_vector cameraOne; + + cameraOne.SetIdentity (m.Cols ()); + + dng_vector xyz = m * cameraOne; + + m = PCStoXYZ ().AsDiagonal () * + Invert (xyz.AsDiagonal ()) * + m; + + } + + } + +/******************************************************************************/ + +void dng_camera_profile::SetForwardMatrix1 (const dng_matrix &m) + { + + fForwardMatrix1 = m; + + fForwardMatrix1.Round (10000); + + ClearFingerprint (); + + } + +/******************************************************************************/ + +void dng_camera_profile::SetForwardMatrix2 (const dng_matrix &m) + { + + fForwardMatrix2 = m; + + fForwardMatrix2.Round (10000); + + ClearFingerprint (); + + } + +/*****************************************************************************/ + +void dng_camera_profile::SetReductionMatrix1 (const dng_matrix &m) + { + + fReductionMatrix1 = m; + + fReductionMatrix1.Round (10000); + + ClearFingerprint (); + + } + +/******************************************************************************/ + +void dng_camera_profile::SetReductionMatrix2 (const dng_matrix &m) + { + + fReductionMatrix2 = m; + + fReductionMatrix2.Round (10000); + + ClearFingerprint (); + + } + +/*****************************************************************************/ + +bool dng_camera_profile::HasColorMatrix1 () const + { + + return fColorMatrix1.Cols () == 3 && + fColorMatrix1.Rows () > 1; + + } + +/*****************************************************************************/ + +bool dng_camera_profile::HasColorMatrix2 () const + { + + return fColorMatrix2.Cols () == 3 && + fColorMatrix2.Rows () == fColorMatrix1.Rows (); + + } + +/*****************************************************************************/ + +void dng_camera_profile::SetHueSatDeltas1 (const dng_hue_sat_map &deltas1) + { + + fHueSatDeltas1 = deltas1; + + ClearFingerprint (); + + } + +/*****************************************************************************/ + +void dng_camera_profile::SetHueSatDeltas2 (const dng_hue_sat_map &deltas2) + { + + fHueSatDeltas2 = deltas2; + + ClearFingerprint (); + + } + +/*****************************************************************************/ + +void dng_camera_profile::SetLookTable (const dng_hue_sat_map &table) + { + + fLookTable = table; + + ClearFingerprint (); + + } + +/*****************************************************************************/ + +static void FingerprintMatrix (dng_md5_printer_stream &printer, + const dng_matrix &matrix) + { + + tag_matrix tag (0, matrix); + + // Tag's Put routine doesn't write the header, only the data + + tag.Put (printer); + + } + +/*****************************************************************************/ + +static void FingerprintHueSatMap (dng_md5_printer_stream &printer, + const dng_hue_sat_map &map) + { + + if (map.IsNull ()) + return; + + uint32 hues; + uint32 sats; + uint32 vals; + + map.GetDivisions (hues, sats, vals); + + printer.Put_uint32 (hues); + printer.Put_uint32 (sats); + printer.Put_uint32 (vals); + + for (uint32 val = 0; val < vals; val++) + for (uint32 hue = 0; hue < hues; hue++) + for (uint32 sat = 0; sat < sats; sat++) + { + + dng_hue_sat_map::HSBModify modify; + + map.GetDelta (hue, sat, val, modify); + + printer.Put_real32 (modify.fHueShift); + printer.Put_real32 (modify.fSatScale); + printer.Put_real32 (modify.fValScale); + + } + + } + +/*****************************************************************************/ + +void dng_camera_profile::CalculateFingerprint () const + { + + DNG_ASSERT (!fWasStubbed, "CalculateFingerprint on stubbed profile"); + + dng_md5_printer_stream printer; + + // MD5 hash is always calculated on little endian data. + + printer.SetLittleEndian (); + + // The data that we fingerprint closely matches that saved + // by the profile_tag_set class in dng_image_writer.cpp, with + // the exception of the fingerprint itself. + + if (HasColorMatrix1 ()) + { + + uint32 colorChannels = ColorMatrix1 ().Rows (); + + printer.Put_uint16 ((uint16) fCalibrationIlluminant1); + + FingerprintMatrix (printer, fColorMatrix1); + + if (fForwardMatrix1.Rows () == fColorMatrix1.Cols () && + fForwardMatrix1.Cols () == fColorMatrix1.Rows ()) + { + + FingerprintMatrix (printer, fForwardMatrix1); + + } + + if (colorChannels > 3 && fReductionMatrix1.Rows () * + fReductionMatrix1.Cols () == colorChannels * 3) + { + + FingerprintMatrix (printer, fReductionMatrix1); + + } + + if (HasColorMatrix2 ()) + { + + printer.Put_uint16 ((uint16) fCalibrationIlluminant2); + + FingerprintMatrix (printer, fColorMatrix2); + + if (fForwardMatrix2.Rows () == fColorMatrix2.Cols () && + fForwardMatrix2.Cols () == fColorMatrix2.Rows ()) + { + + FingerprintMatrix (printer, fForwardMatrix2); + + } + + if (colorChannels > 3 && fReductionMatrix2.Rows () * + fReductionMatrix2.Cols () == colorChannels * 3) + { + + FingerprintMatrix (printer, fReductionMatrix2); + + } + + } + + printer.Put (fName.Get (), + fName.Length ()); + + printer.Put (fProfileCalibrationSignature.Get (), + fProfileCalibrationSignature.Length ()); + + printer.Put_uint32 (fEmbedPolicy); + + printer.Put (fCopyright.Get (), + fCopyright.Length ()); + + bool haveHueSat1 = HueSatDeltas1 ().IsValid (); + + bool haveHueSat2 = HueSatDeltas2 ().IsValid () && + HasColorMatrix2 (); + + if (haveHueSat1) + { + + FingerprintHueSatMap (printer, fHueSatDeltas1); + + } + + if (haveHueSat2) + { + + FingerprintHueSatMap (printer, fHueSatDeltas2); + + } + + if (haveHueSat1 || haveHueSat2) + { + + if (fHueSatMapEncoding != 0) + { + + printer.Put_uint32 (fHueSatMapEncoding); + + } + + } + + if (fLookTable.IsValid ()) + { + + FingerprintHueSatMap (printer, fLookTable); + + if (fLookTableEncoding != 0) + { + + printer.Put_uint32 (fLookTableEncoding); + + } + + } + + if (fBaselineExposureOffset.IsValid ()) + { + + if (fBaselineExposureOffset.As_real64 () != 0.0) + { + + printer.Put_real64 (fBaselineExposureOffset.As_real64 ()); + + } + + } + + if (fDefaultBlackRender != 0) + { + + printer.Put_int32 (fDefaultBlackRender); + + } + + if (fToneCurve.IsValid ()) + { + + for (uint32 i = 0; i < fToneCurve.fCoord.size (); i++) + { + + printer.Put_real32 ((real32) fToneCurve.fCoord [i].h); + printer.Put_real32 ((real32) fToneCurve.fCoord [i].v); + + } + + } + + } + + fFingerprint = printer.Result (); + + } + +/******************************************************************************/ + +bool dng_camera_profile::ValidForwardMatrix (const dng_matrix &m) + { + + const real64 kThreshold = 0.01; + + if (m.NotEmpty ()) + { + + dng_vector cameraOne; + + cameraOne.SetIdentity (m.Cols ()); + + dng_vector xyz = m * cameraOne; + + dng_vector pcs = PCStoXYZ (); + + if (Abs_real64 (xyz [0] - pcs [0]) > kThreshold || + Abs_real64 (xyz [1] - pcs [1]) > kThreshold || + Abs_real64 (xyz [2] - pcs [2]) > kThreshold) + { + + return false; + + } + + } + + return true; + + } + +/******************************************************************************/ + +bool dng_camera_profile::IsValid (uint32 channels) const + { + + // For Monochrome images, we ignore the camera profile. + + if (channels == 1) + { + + return true; + + } + + // ColorMatrix1 is required for all color images. + + if (fColorMatrix1.Cols () != 3 || + fColorMatrix1.Rows () != channels) + { + + #if qDNGValidate + + ReportError ("ColorMatrix1 is wrong size"); + + #endif + + return false; + + } + + // ColorMatrix2 is optional, but it must be valid if present. + + if (fColorMatrix2.Cols () != 0 || + fColorMatrix2.Rows () != 0) + { + + if (fColorMatrix2.Cols () != 3 || + fColorMatrix2.Rows () != channels) + { + + #if qDNGValidate + + ReportError ("ColorMatrix2 is wrong size"); + + #endif + + return false; + + } + + } + + // ForwardMatrix1 is optional, but it must be valid if present. + + if (fForwardMatrix1.Cols () != 0 || + fForwardMatrix1.Rows () != 0) + { + + if (fForwardMatrix1.Rows () != 3 || + fForwardMatrix1.Cols () != channels) + { + + #if qDNGValidate + + ReportError ("ForwardMatrix1 is wrong size"); + + #endif + + return false; + + } + + // Make sure ForwardMatrix1 does a valid mapping. + + if (!ValidForwardMatrix (fForwardMatrix1)) + { + + #if qDNGValidate + + ReportError ("ForwardMatrix1 does not map equal camera values to XYZ D50"); + + #endif + + return false; + + } + + } + + // ForwardMatrix2 is optional, but it must be valid if present. + + if (fForwardMatrix2.Cols () != 0 || + fForwardMatrix2.Rows () != 0) + { + + if (fForwardMatrix2.Rows () != 3 || + fForwardMatrix2.Cols () != channels) + { + + #if qDNGValidate + + ReportError ("ForwardMatrix2 is wrong size"); + + #endif + + return false; + + } + + // Make sure ForwardMatrix2 does a valid mapping. + + if (!ValidForwardMatrix (fForwardMatrix2)) + { + + #if qDNGValidate + + ReportError ("ForwardMatrix2 does not map equal camera values to XYZ D50"); + + #endif + + return false; + + } + + } + + // ReductionMatrix1 is optional, but it must be valid if present. + + if (fReductionMatrix1.Cols () != 0 || + fReductionMatrix1.Rows () != 0) + { + + if (fReductionMatrix1.Cols () != channels || + fReductionMatrix1.Rows () != 3) + { + + #if qDNGValidate + + ReportError ("ReductionMatrix1 is wrong size"); + + #endif + + return false; + + } + + } + + // ReductionMatrix2 is optional, but it must be valid if present. + + if (fReductionMatrix2.Cols () != 0 || + fReductionMatrix2.Rows () != 0) + { + + if (fReductionMatrix2.Cols () != channels || + fReductionMatrix2.Rows () != 3) + { + + #if qDNGValidate + + ReportError ("ReductionMatrix2 is wrong size"); + + #endif + + return false; + + } + + } + + // Make sure ColorMatrix1 is invertable. + + try + { + + if (fReductionMatrix1.NotEmpty ()) + { + + (void) Invert (fColorMatrix1, + fReductionMatrix1); + + } + + else + { + + (void) Invert (fColorMatrix1); + + } + + } + + catch (...) + { + + #if qDNGValidate + + ReportError ("ColorMatrix1 is not invertable"); + + #endif + + return false; + + } + + // Make sure ColorMatrix2 is invertable. + + if (fColorMatrix2.NotEmpty ()) + { + + try + { + + if (fReductionMatrix2.NotEmpty ()) + { + + (void) Invert (fColorMatrix2, + fReductionMatrix2); + + } + + else + { + + (void) Invert (fColorMatrix2); + + } + + } + + catch (...) + { + + #if qDNGValidate + + ReportError ("ColorMatrix2 is not invertable"); + + #endif + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_camera_profile::EqualData (const dng_camera_profile &profile) const + { + + return fCalibrationIlluminant1 == profile.fCalibrationIlluminant1 && + fCalibrationIlluminant2 == profile.fCalibrationIlluminant2 && + fColorMatrix1 == profile.fColorMatrix1 && + fColorMatrix2 == profile.fColorMatrix2 && + fForwardMatrix1 == profile.fForwardMatrix1 && + fForwardMatrix2 == profile.fForwardMatrix2 && + fReductionMatrix1 == profile.fReductionMatrix1 && + fReductionMatrix2 == profile.fReductionMatrix2 && + fHueSatDeltas1 == profile.fHueSatDeltas1 && + fHueSatDeltas2 == profile.fHueSatDeltas2 && + fHueSatMapEncoding == profile.fHueSatMapEncoding && + fLookTable == profile.fLookTable && + fLookTableEncoding == profile.fLookTableEncoding && + fDefaultBlackRender == profile.fDefaultBlackRender && + fToneCurve == profile.fToneCurve && + fBaselineExposureOffset.As_real64 () == profile.fBaselineExposureOffset.As_real64 () && + fProfileCalibrationSignature == profile.fProfileCalibrationSignature; + + } + +/*****************************************************************************/ + +void dng_camera_profile::ReadHueSatMap (dng_stream &stream, + dng_hue_sat_map &hueSatMap, + uint32 hues, + uint32 sats, + uint32 vals, + bool skipSat0) + { + + hueSatMap.SetDivisions (hues, sats, vals); + + for (uint32 val = 0; val < vals; val++) + { + + for (uint32 hue = 0; hue < hues; hue++) + { + + for (uint32 sat = skipSat0 ? 1 : 0; sat < sats; sat++) + { + + dng_hue_sat_map::HSBModify modify; + + modify.fHueShift = stream.Get_real32 (); + modify.fSatScale = stream.Get_real32 (); + modify.fValScale = stream.Get_real32 (); + + hueSatMap.SetDelta (hue, sat, val, modify); + + } + + } + + } + + } + +/*****************************************************************************/ + +void dng_camera_profile::Parse (dng_stream &stream, + dng_camera_profile_info &profileInfo) + { + + SetUniqueCameraModelRestriction (profileInfo.fUniqueCameraModel.Get ()); + + if (profileInfo.fProfileName.NotEmpty ()) + { + + SetName (profileInfo.fProfileName.Get ()); + + } + + SetCopyright (profileInfo.fProfileCopyright.Get ()); + + SetEmbedPolicy (profileInfo.fEmbedPolicy); + + SetCalibrationIlluminant1 (profileInfo.fCalibrationIlluminant1); + + SetColorMatrix1 (profileInfo.fColorMatrix1); + + if (profileInfo.fForwardMatrix1.NotEmpty ()) + { + + SetForwardMatrix1 (profileInfo.fForwardMatrix1); + + } + + if (profileInfo.fReductionMatrix1.NotEmpty ()) + { + + SetReductionMatrix1 (profileInfo.fReductionMatrix1); + + } + + if (profileInfo.fColorMatrix2.NotEmpty ()) + { + + SetCalibrationIlluminant2 (profileInfo.fCalibrationIlluminant2); + + SetColorMatrix2 (profileInfo.fColorMatrix2); + + if (profileInfo.fForwardMatrix2.NotEmpty ()) + { + + SetForwardMatrix2 (profileInfo.fForwardMatrix2); + + } + + if (profileInfo.fReductionMatrix2.NotEmpty ()) + { + + SetReductionMatrix2 (profileInfo.fReductionMatrix2); + + } + + } + + SetProfileCalibrationSignature (profileInfo.fProfileCalibrationSignature.Get ()); + + if (profileInfo.fHueSatDeltas1Offset != 0 && + profileInfo.fHueSatDeltas1Count != 0) + { + + TempBigEndian setEndianness (stream, profileInfo.fBigEndian); + + stream.SetReadPosition (profileInfo.fHueSatDeltas1Offset); + + bool skipSat0 = (profileInfo.fHueSatDeltas1Count == profileInfo.fProfileHues * + (profileInfo.fProfileSats - 1) * + profileInfo.fProfileVals * 3); + + ReadHueSatMap (stream, + fHueSatDeltas1, + profileInfo.fProfileHues, + profileInfo.fProfileSats, + profileInfo.fProfileVals, + skipSat0); + + } + + if (profileInfo.fHueSatDeltas2Offset != 0 && + profileInfo.fHueSatDeltas2Count != 0) + { + + TempBigEndian setEndianness (stream, profileInfo.fBigEndian); + + stream.SetReadPosition (profileInfo.fHueSatDeltas2Offset); + + bool skipSat0 = (profileInfo.fHueSatDeltas2Count == profileInfo.fProfileHues * + (profileInfo.fProfileSats - 1) * + profileInfo.fProfileVals * 3); + + ReadHueSatMap (stream, + fHueSatDeltas2, + profileInfo.fProfileHues, + profileInfo.fProfileSats, + profileInfo.fProfileVals, + skipSat0); + + } + + if (profileInfo.fLookTableOffset != 0 && + profileInfo.fLookTableCount != 0) + { + + TempBigEndian setEndianness (stream, profileInfo.fBigEndian); + + stream.SetReadPosition (profileInfo.fLookTableOffset); + + bool skipSat0 = (profileInfo.fLookTableCount == profileInfo.fLookTableHues * + (profileInfo.fLookTableSats - 1) * + profileInfo.fLookTableVals * 3); + + ReadHueSatMap (stream, + fLookTable, + profileInfo.fLookTableHues, + profileInfo.fLookTableSats, + profileInfo.fLookTableVals, + skipSat0); + + } + + if ((profileInfo.fToneCurveCount & 1) == 0) + { + + TempBigEndian setEndianness (stream, profileInfo.fBigEndian); + + stream.SetReadPosition (profileInfo.fToneCurveOffset); + + uint32 points = profileInfo.fToneCurveCount / 2; + + fToneCurve.fCoord.resize (points); + + for (size_t i = 0; i < points; i++) + { + + dng_point_real64 point; + + point.h = stream.Get_real32 (); + point.v = stream.Get_real32 (); + + fToneCurve.fCoord [i] = point; + + } + + } + + SetHueSatMapEncoding (profileInfo.fHueSatMapEncoding); + + SetLookTableEncoding (profileInfo.fLookTableEncoding); + + SetBaselineExposureOffset (profileInfo.fBaselineExposureOffset.As_real64 ()); + + SetDefaultBlackRender (profileInfo.fDefaultBlackRender); + + } + +/*****************************************************************************/ + +bool dng_camera_profile::ParseExtended (dng_stream &stream) + { + + try + { + + dng_camera_profile_info profileInfo; + + if (!profileInfo.ParseExtended (stream)) + { + return false; + } + + Parse (stream, profileInfo); + + return true; + + } + + catch (...) + { + + // Eat parsing errors. + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_camera_profile::SetFourColorBayer () + { + + uint32 j; + + if (!IsValid (3)) + { + ThrowProgramError (); + } + + if (fColorMatrix1.NotEmpty ()) + { + + dng_matrix m (4, 3); + + for (j = 0; j < 3; j++) + { + m [0] [j] = fColorMatrix1 [0] [j]; + m [1] [j] = fColorMatrix1 [1] [j]; + m [2] [j] = fColorMatrix1 [2] [j]; + m [3] [j] = fColorMatrix1 [1] [j]; + } + + fColorMatrix1 = m; + + } + + if (fColorMatrix2.NotEmpty ()) + { + + dng_matrix m (4, 3); + + for (j = 0; j < 3; j++) + { + m [0] [j] = fColorMatrix2 [0] [j]; + m [1] [j] = fColorMatrix2 [1] [j]; + m [2] [j] = fColorMatrix2 [2] [j]; + m [3] [j] = fColorMatrix2 [1] [j]; + } + + fColorMatrix2 = m; + + } + + fReductionMatrix1.Clear (); + fReductionMatrix2.Clear (); + + fForwardMatrix1.Clear (); + fForwardMatrix2.Clear (); + + } + +/*****************************************************************************/ + +dng_hue_sat_map * dng_camera_profile::HueSatMapForWhite (const dng_xy_coord &white) const + { + + if (fHueSatDeltas1.IsValid ()) + { + + // If we only have the first table, just use it for any color temperature. + + if (!fHueSatDeltas2.IsValid ()) + { + + return new dng_hue_sat_map (fHueSatDeltas1); + + } + + // Else we need to interpolate based on color temperature. + + real64 temperature1 = CalibrationTemperature1 (); + real64 temperature2 = CalibrationTemperature2 (); + + if (temperature1 <= 0.0 || + temperature2 <= 0.0 || + temperature1 == temperature2) + { + + return new dng_hue_sat_map (fHueSatDeltas1); + + } + + bool reverseOrder = temperature1 > temperature2; + + if (reverseOrder) + { + real64 temp = temperature1; + temperature1 = temperature2; + temperature2 = temp; + } + + // Convert to temperature/offset space. + + dng_temperature td (white); + + // Find fraction to weight the first calibration. + + real64 g; + + if (td.Temperature () <= temperature1) + g = 1.0; + + else if (td.Temperature () >= temperature2) + g = 0.0; + + else + { + + real64 invT = 1.0 / td.Temperature (); + + g = (invT - (1.0 / temperature2)) / + ((1.0 / temperature1) - (1.0 / temperature2)); + + } + + // Fix up if we swapped the order. + + if (reverseOrder) + { + g = 1.0 - g; + } + + // Do the interpolation. + + return dng_hue_sat_map::Interpolate (HueSatDeltas1 (), + HueSatDeltas2 (), + g); + + } + + return NULL; + + } + +/*****************************************************************************/ + +void dng_camera_profile::Stub () + { + + (void) Fingerprint (); + + dng_hue_sat_map nullTable; + + fHueSatDeltas1 = nullTable; + fHueSatDeltas2 = nullTable; + + fLookTable = nullTable; + + fToneCurve.SetInvalid (); + + fWasStubbed = true; + + } + +/*****************************************************************************/ + +void SplitCameraProfileName (const dng_string &name, + dng_string &baseName, + int32 &version) + { + + baseName = name; + + version = 0; + + uint32 len = baseName.Length (); + + if (len > 5 && baseName.EndsWith (" beta")) + { + + baseName.Truncate (len - 5); + + version += -10; + + } + + else if (len > 7) + { + + char lastChar = name.Get () [len - 1]; + + if (lastChar >= '0' && lastChar <= '9') + { + + dng_string temp = name; + + temp.Truncate (len - 1); + + if (temp.EndsWith (" beta ")) + { + + baseName.Truncate (len - 7); + + version += ((int32) (lastChar - '0')) - 10; + + } + + } + + } + + len = baseName.Length (); + + if (len > 3) + { + + char lastChar = name.Get () [len - 1]; + + if (lastChar >= '0' && lastChar <= '9') + { + + dng_string temp = name; + + temp.Truncate (len - 1); + + if (temp.EndsWith (" v")) + { + + baseName.Truncate (len - 3); + + version += ((int32) (lastChar - '0')) * 100; + + } + + } + + } + + } + +/*****************************************************************************/ + +void BuildHueSatMapEncodingTable (dng_memory_allocator &allocator, + uint32 encoding, + AutoPtr &encodeTable, + AutoPtr &decodeTable, + bool subSample) + { + + encodeTable.Reset (); + decodeTable.Reset (); + + switch (encoding) + { + + case encoding_Linear: + { + + break; + + } + + case encoding_sRGB: + { + + encodeTable.Reset (new dng_1d_table); + decodeTable.Reset (new dng_1d_table); + + const dng_1d_function & curve = dng_function_GammaEncode_sRGB::Get (); + + encodeTable->Initialize (allocator, + curve, + subSample); + + const dng_1d_inverse inverse (curve); + + decodeTable->Initialize (allocator, + inverse, + subSample); + + break; + + } + + default: + { + + DNG_REPORT ("Unsupported hue sat map / look table encoding."); + + break; + + } + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_camera_profile.h b/source/lib/dng_sdk/dng_camera_profile.h new file mode 100644 index 0000000..df14a8a --- /dev/null +++ b/source/lib/dng_sdk/dng_camera_profile.h @@ -0,0 +1,878 @@ +/******************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/******************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_camera_profile.h#2 $ */ +/* $DateTime: 2012/07/11 10:36:56 $ */ +/* $Change: 838485 $ */ +/* $Author: tknoll $ */ + +/** \file + * Support for DNG camera color profile information. + * Per the \ref spec_dng "DNG 1.1.0 specification", a DNG file can store up to + * two sets of color profile information for a camera in the DNG file from that + * camera. The second set is optional and when there are two sets, they represent + * profiles made under different illumination. + * + * Profiling information is optionally separated into two parts. One part represents + * a profile for a reference camera. (ColorMatrix1 and ColorMatrix2 here.) The + * second is a per-camera calibration that takes into account unit-to-unit variation. + * This is designed to allow replacing the reference color matrix with one of one's + * own construction while maintaining any unit-specific calibration the camera + * manufacturer may have provided. + * + * See Appendix 6 of the \ref spec_dng "DNG 1.1.0 specification" for more information. + */ + +#ifndef __dng_camera_profile__ +#define __dng_camera_profile__ + +/******************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_assertions.h" +#include "dng_classes.h" +#include "dng_fingerprint.h" +#include "dng_hue_sat_map.h" +#include "dng_matrix.h" +#include "dng_string.h" +#include "dng_tag_values.h" +#include "dng_tone_curve.h" + +/******************************************************************************/ + +extern const char * kProfileName_Embedded; + +extern const char * kAdobeCalibrationSignature; + +/******************************************************************************/ + +/// \brief An ID for a camera profile consisting of a name and optional fingerprint. + +class dng_camera_profile_id + { + + private: + + dng_string fName; + + dng_fingerprint fFingerprint; + + public: + + /// Construct an invalid camera profile ID (empty name and fingerprint). + + dng_camera_profile_id () + + : fName () + , fFingerprint () + + { + } + + /// Construct a camera profile ID with the specified name and no fingerprint. + /// \param name The name of the camera profile ID. + + dng_camera_profile_id (const char *name) + + : fName () + , fFingerprint () + + { + fName.Set (name); + } + + /// Construct a camera profile ID with the specified name and no fingerprint. + /// \param name The name of the camera profile ID. + + dng_camera_profile_id (const dng_string &name) + + : fName (name) + , fFingerprint () + + { + } + + /// Construct a camera profile ID with the specified name and fingerprint. + /// \param name The name of the camera profile ID. + /// \param fingerprint The fingerprint of the camera profile ID. + + dng_camera_profile_id (const char *name, + const dng_fingerprint &fingerprint) + + : fName () + , fFingerprint (fingerprint) + + { + fName.Set (name); + DNG_ASSERT (!fFingerprint.IsValid () || fName.NotEmpty (), + "Cannot have profile fingerprint without name"); + } + + /// Construct a camera profile ID with the specified name and fingerprint. + /// \param name The name of the camera profile ID. + /// \param fingerprint The fingerprint of the camera profile ID. + + dng_camera_profile_id (const dng_string &name, + const dng_fingerprint &fingerprint) + + : fName (name) + , fFingerprint (fingerprint) + + { + DNG_ASSERT (!fFingerprint.IsValid () || fName.NotEmpty (), + "Cannot have profile fingerprint without name"); + } + + /// Getter for the name of the camera profile ID. + /// \retval The name of the camera profile ID. + + const dng_string & Name () const + { + return fName; + } + + /// Getter for the fingerprint of the camera profile ID. + /// \retval The fingerprint of the camera profile ID. + + const dng_fingerprint & Fingerprint () const + { + return fFingerprint; + } + + /// Test for equality of two camera profile IDs. + /// \param The id of the camera profile ID to compare. + + bool operator== (const dng_camera_profile_id &id) const + { + return fName == id.fName && + fFingerprint == id.fFingerprint; + } + + /// Test for inequality of two camera profile IDs. + /// \param The id of the camera profile ID to compare. + + bool operator!= (const dng_camera_profile_id &id) const + { + return !(*this == id); + } + + /// Returns true iff the camera profile ID is valid. + + bool IsValid () const + { + return fName.NotEmpty (); // Fingerprint is optional. + } + + /// Resets the name and fingerprint, thereby making this camera profile ID + /// invalid. + + void Clear () + { + *this = dng_camera_profile_id (); + } + + }; + +/******************************************************************************/ + +/// \brief Container for DNG camera color profile and calibration data. + +class dng_camera_profile + { + + protected: + + // Name of this camera profile. + + dng_string fName; + + // Light sources for up to two calibrations. These use the EXIF + // encodings for illuminant and are used to distinguish which + // matrix to use. + + uint32 fCalibrationIlluminant1; + uint32 fCalibrationIlluminant2; + + // Color matrices for up to two calibrations. + + // These matrices map XYZ values to non-white balanced camera values. + // Adobe needs to go that direction in order to determine the clipping + // points for highlight recovery logic based on the white point. If + // cameras were all 3-color, the matrix could be stored as a forward matrix, + // but we need the backwards matrix to deal with 4-color cameras. + + dng_matrix fColorMatrix1; + dng_matrix fColorMatrix2; + + // These matrices map white balanced camera values to XYZ chromatically + // adapted to D50 (the ICC profile PCS white point). If the matrices + // exist, then this implies that white balancing should be done by scaling + // camera values with a diagonal matrix. + + dng_matrix fForwardMatrix1; + dng_matrix fForwardMatrix2; + + // Dimensionality reduction hints for more than three color cameras. + // This is an optional matrix that maps the camera's color components + // to 3 components. These are only used if the forward matrices don't + // exist, and are used invert the color matrices. + + dng_matrix fReductionMatrix1; + dng_matrix fReductionMatrix2; + + // MD5 hash for all data bits of the profile. + + mutable dng_fingerprint fFingerprint; + + // Copyright notice from creator of profile. + + dng_string fCopyright; + + // Rules for how this profile can be embedded and/or copied. + + uint32 fEmbedPolicy; + + // 2-D (or 3-D) hue/sat tables to modify colors. + + dng_hue_sat_map fHueSatDeltas1; + dng_hue_sat_map fHueSatDeltas2; + + // Value (V of HSV) encoding for hue/sat tables. + + uint32 fHueSatMapEncoding; + + // 3-D hue/sat table to apply a "look". + + dng_hue_sat_map fLookTable; + + // Value (V of HSV) encoding for look table. + + uint32 fLookTableEncoding; + + // Baseline exposure offset. When using this profile, this offset value is + // added to the BaselineExposure value for the negative to determine the + // overall baseline exposure to apply. + + dng_srational fBaselineExposureOffset; + + // Default black rendering. + + uint32 fDefaultBlackRender; + + // The "as shot" tone curve for this profile. Check IsValid method + // to tell if one exists in profile. + + dng_tone_curve fToneCurve; + + // If this string matches the fCameraCalibrationSignature of the + // negative, then use the calibration matrix values from the negative. + + dng_string fProfileCalibrationSignature; + + // If non-empty, only allow use of this profile with camera having + // same unique model name. + + dng_string fUniqueCameraModelRestriction; + + // Was this profile read from inside a DNG file? (If so, we wnat + // to be sure to include it again when writing out an updated + // DNG file) + + bool fWasReadFromDNG; + + // Was this profile read from disk (i.e., an external profile)? (If so, we + // may need to refresh when changes are made externally to the profile + // directory.) + + bool fWasReadFromDisk; + + // Was this profile a built-in "Matrix" profile? (If so, we may need to + // refresh -- i.e., remove it from the list of available profiles -- when + // changes are made externally to the profile directory.) + + bool fWasBuiltinMatrix; + + // Was this profile stubbed to save memory (and no longer valid + // for building color conversion tables)? + + bool fWasStubbed; + + public: + + dng_camera_profile (); + + virtual ~dng_camera_profile (); + + // API for profile name: + + /// Setter for camera profile name. + /// \param name Name to use for this camera profile. + + void SetName (const char *name) + { + fName.Set (name); + ClearFingerprint (); + } + + /// Getter for camera profile name. + /// \retval Name of profile. + + const dng_string & Name () const + { + return fName; + } + + /// Test if this name is embedded. + /// \retval true if the name matches the name of the embedded camera profile. + + bool NameIsEmbedded () const + { + return fName.Matches (kProfileName_Embedded, true); + } + + // API for calibration illuminants: + + /// Setter for first of up to two light sources used for calibration. + /// Uses the EXIF encodings for illuminant and is used to distinguish which + /// matrix to use. + /// Corresponds to the DNG CalibrationIlluminant1 tag. + + void SetCalibrationIlluminant1 (uint32 light) + { + fCalibrationIlluminant1 = light; + ClearFingerprint (); + } + + /// Setter for second of up to two light sources used for calibration. + /// Uses the EXIF encodings for illuminant and is used to distinguish which + /// matrix to use. + /// Corresponds to the DNG CalibrationIlluminant2 tag. + + void SetCalibrationIlluminant2 (uint32 light) + { + fCalibrationIlluminant2 = light; + ClearFingerprint (); + } + + /// Getter for first of up to two light sources used for calibration. + /// Uses the EXIF encodings for illuminant and is used to distinguish which + /// matrix to use. + /// Corresponds to the DNG CalibrationIlluminant1 tag. + + uint32 CalibrationIlluminant1 () const + { + return fCalibrationIlluminant1; + } + + /// Getter for second of up to two light sources used for calibration. + /// Uses the EXIF encodings for illuminant and is used to distinguish which + /// matrix to use. + /// Corresponds to the DNG CalibrationIlluminant2 tag. + + uint32 CalibrationIlluminant2 () const + { + return fCalibrationIlluminant2; + } + + /// Getter for first of up to two light sources used for calibration, returning + /// result as color temperature. + + real64 CalibrationTemperature1 () const + { + return IlluminantToTemperature (CalibrationIlluminant1 ()); + } + + /// Getter for second of up to two light sources used for calibration, returning + /// result as color temperature. + + real64 CalibrationTemperature2 () const + { + return IlluminantToTemperature (CalibrationIlluminant2 ()); + } + + // API for color matrices: + + /// Utility function to normalize the scale of the color matrix. + + static void NormalizeColorMatrix (dng_matrix &m); + + /// Setter for first of up to two color matrices used for reference camera calibrations. + /// These matrices map XYZ values to camera values. The DNG SDK needs to map colors + /// that direction in order to determine the clipping points for + /// highlight recovery logic based on the white point. If cameras + /// were all three-color, the matrix could be stored as a forward matrix. + /// The inverse matrix is requried to support four-color cameras. + + void SetColorMatrix1 (const dng_matrix &m); + + /// Setter for second of up to two color matrices used for reference camera calibrations. + /// These matrices map XYZ values to camera values. The DNG SDK needs to map colors + /// that direction in order to determine the clipping points for + /// highlight recovery logic based on the white point. If cameras + /// were all three-color, the matrix could be stored as a forward matrix. + /// The inverse matrix is requried to support four-color cameras. + + void SetColorMatrix2 (const dng_matrix &m); + + /// Predicate to test if first camera matrix is set + + bool HasColorMatrix1 () const; + + /// Predicate to test if second camera matrix is set + + bool HasColorMatrix2 () const; + + /// Getter for first of up to two color matrices used for calibrations. + + const dng_matrix & ColorMatrix1 () const + { + return fColorMatrix1; + } + + /// Getter for second of up to two color matrices used for calibrations. + + const dng_matrix & ColorMatrix2 () const + { + return fColorMatrix2; + } + + // API for forward matrices: + + /// Utility function to normalize the scale of the forward matrix. + + static void NormalizeForwardMatrix (dng_matrix &m); + + /// Setter for first of up to two forward matrices used for calibrations. + + void SetForwardMatrix1 (const dng_matrix &m); + + /// Setter for second of up to two forward matrices used for calibrations. + + void SetForwardMatrix2 (const dng_matrix &m); + + /// Getter for first of up to two forward matrices used for calibrations. + + const dng_matrix & ForwardMatrix1 () const + { + return fForwardMatrix1; + } + + /// Getter for second of up to two forward matrices used for calibrations. + + const dng_matrix & ForwardMatrix2 () const + { + return fForwardMatrix2; + } + + // API for reduction matrices: + + /// Setter for first of up to two dimensionality reduction hints for four-color cameras. + /// This is an optional matrix that maps four components to three. + /// See Appendix 6 of the \ref spec_dng "DNG 1.1.0 specification." + + void SetReductionMatrix1 (const dng_matrix &m); + + /// Setter for second of up to two dimensionality reduction hints for four-color cameras. + /// This is an optional matrix that maps four components to three. + /// See Appendix 6 of the \ref spec_dng "DNG 1.1.0 specification." + + void SetReductionMatrix2 (const dng_matrix &m); + + /// Getter for first of up to two dimensionality reduction hints for four color cameras. + + const dng_matrix & ReductionMatrix1 () const + { + return fReductionMatrix1; + } + + /// Getter for second of up to two dimensionality reduction hints for four color cameras. + + const dng_matrix & ReductionMatrix2 () const + { + return fReductionMatrix2; + } + + /// Getter function from profile fingerprint. + + const dng_fingerprint &Fingerprint () const + { + + if (!fFingerprint.IsValid ()) + CalculateFingerprint (); + + return fFingerprint; + + } + + /// Getter for camera profile id. + /// \retval ID of profile. + + dng_camera_profile_id ProfileID () const + { + return dng_camera_profile_id (Name (), Fingerprint ()); + } + + /// Setter for camera profile copyright. + /// \param copyright Copyright string to use for this camera profile. + + void SetCopyright (const char *copyright) + { + fCopyright.Set (copyright); + ClearFingerprint (); + } + + /// Getter for camera profile copyright. + /// \retval Copyright string for profile. + + const dng_string & Copyright () const + { + return fCopyright; + } + + // Accessors for embed policy. + + /// Setter for camera profile embed policy. + /// \param policy Policy to use for this camera profile. + + void SetEmbedPolicy (uint32 policy) + { + fEmbedPolicy = policy; + ClearFingerprint (); + } + + /// Getter for camera profile embed policy. + /// \param Policy for profile. + + uint32 EmbedPolicy () const + { + return fEmbedPolicy; + } + + /// Returns true iff the profile is legal to embed in a DNG, per the + /// profile's embed policy. + + bool IsLegalToEmbed () const + { + return WasReadFromDNG () || + EmbedPolicy () == pepAllowCopying || + EmbedPolicy () == pepEmbedIfUsed || + EmbedPolicy () == pepNoRestrictions; + } + + // Accessors for hue sat maps. + + /// Returns true iff the profile has a valid HueSatMap color table. + + bool HasHueSatDeltas () const + { + return fHueSatDeltas1.IsValid (); + } + + /// Getter for first HueSatMap color table (for calibration illuminant 1). + + const dng_hue_sat_map & HueSatDeltas1 () const + { + return fHueSatDeltas1; + } + + /// Setter for first HueSatMap color table (for calibration illuminant 1). + + void SetHueSatDeltas1 (const dng_hue_sat_map &deltas1); + + /// Getter for second HueSatMap color table (for calibration illuminant 2). + + const dng_hue_sat_map & HueSatDeltas2 () const + { + return fHueSatDeltas2; + } + + /// Setter for second HueSatMap color table (for calibration illuminant 2). + + void SetHueSatDeltas2 (const dng_hue_sat_map &deltas2); + + // Accessors for hue sat map encoding. + + /// Returns the hue sat map encoding (see ProfileHueSatMapEncoding tag). + + uint32 HueSatMapEncoding () const + { + return fHueSatMapEncoding; + } + + /// Sets the hue sat map encoding (see ProfileHueSatMapEncoding tag) to the + /// specified encoding. + + void SetHueSatMapEncoding (uint32 encoding) + { + fHueSatMapEncoding = encoding; + ClearFingerprint (); + } + + // Accessors for look table. + + /// Returns true if the profile has a LookTable. + + bool HasLookTable () const + { + return fLookTable.IsValid (); + } + + /// Getter for LookTable. + + const dng_hue_sat_map & LookTable () const + { + return fLookTable; + } + + /// Setter for LookTable. + + void SetLookTable (const dng_hue_sat_map &table); + + // Accessors for look table encoding. + + /// Returns the LookTable encoding (see ProfileLookTableEncoding tag). + + uint32 LookTableEncoding () const + { + return fLookTableEncoding; + } + + /// Sets the LookTable encoding (see ProfileLookTableEncoding tag) to the + /// specified encoding. + + void SetLookTableEncoding (uint32 encoding) + { + fLookTableEncoding = encoding; + ClearFingerprint (); + } + + // Accessors for baseline exposure offset. + + /// Sets the baseline exposure offset of the profile (see + /// BaselineExposureOffset tag) to the specified value. + + void SetBaselineExposureOffset (real64 exposureOffset) + { + fBaselineExposureOffset.Set_real64 (exposureOffset, 100); + ClearFingerprint (); + } + + /// Returns the baseline exposure offset of the profile (see + /// BaselineExposureOffset tag). + + const dng_srational & BaselineExposureOffset () const + { + return fBaselineExposureOffset; + } + + // Accessors for default black render. + + /// Sets the default black render of the profile (see DefaultBlackRender tag) + /// to the specified option. + + void SetDefaultBlackRender (uint32 defaultBlackRender) + { + fDefaultBlackRender = defaultBlackRender; + ClearFingerprint (); + } + + /// Returns the default black render of the profile (see DefaultBlackRender + /// tag). + + uint32 DefaultBlackRender () const + { + return fDefaultBlackRender; + } + + // Accessors for tone curve. + + /// Returns the tone curve of the profile. + + const dng_tone_curve & ToneCurve () const + { + return fToneCurve; + } + + /// Sets the tone curve of the profile to the specified curve. + + void SetToneCurve (const dng_tone_curve &curve) + { + fToneCurve = curve; + ClearFingerprint (); + } + + // Accessors for profile calibration signature. + + /// Sets the profile calibration signature (see ProfileCalibrationSignature + /// tag) to the specified string. + + void SetProfileCalibrationSignature (const char *signature) + { + fProfileCalibrationSignature.Set (signature); + } + + /// Returns the profile calibration signature (see ProfileCalibrationSignature + /// tag) of the profile. + + const dng_string & ProfileCalibrationSignature () const + { + return fProfileCalibrationSignature; + } + + /// Setter for camera unique model name to restrict use of this profile. + /// \param camera Camera unique model name designating only camera this + /// profile can be used with. (Empty string for no restriction.) + + void SetUniqueCameraModelRestriction (const char *camera) + { + fUniqueCameraModelRestriction.Set (camera); + // Not included in fingerprint, so don't need ClearFingerprint (). + } + + /// Getter for camera unique model name to restrict use of this profile. + /// \retval Unique model name of only camera this profile can be used with + /// or empty if no restriction. + + const dng_string & UniqueCameraModelRestriction () const + { + return fUniqueCameraModelRestriction; + } + + // Accessors for was read from DNG flag. + + /// Sets internal flag to indicate this profile was originally read from a + /// DNG file. + + void SetWasReadFromDNG (bool state = true) + { + fWasReadFromDNG = state; + } + + /// Was this profile read from a DNG? + + bool WasReadFromDNG () const + { + return fWasReadFromDNG; + } + + // Accessors for was read from disk flag. + + /// Sets internal flag to indicate this profile was originally read from + /// disk. + + void SetWasReadFromDisk (bool state = true) + { + fWasReadFromDisk = state; + } + + /// Was this profile read from disk? + + bool WasReadFromDisk () const + { + return fWasReadFromDisk; + } + + // Accessors for was built-in matrix flag. + + /// Sets internal flag to indicate this profile was originally a built-in + /// matrix profile. + + void SetWasBuiltinMatrix (bool state = true) + { + fWasBuiltinMatrix = state; + } + + /// Was this profile a built-in matrix profile? + + bool WasBuiltinMatrix () const + { + return fWasBuiltinMatrix; + } + + /// Determines if this a valid profile for this number of color channels? + /// \retval true if the profile is valid. + + bool IsValid (uint32 channels) const; + + /// Predicate to check if two camera profiles are colorwise equal, thus ignores + /// the profile name. + /// \param profile Camera profile to compare to. + + bool EqualData (const dng_camera_profile &profile) const; + + /// Parse profile from dng_camera_profile_info data. + + void Parse (dng_stream &stream, + dng_camera_profile_info &profileInfo); + + /// Parse from an extended profile stream, which is similar to stand alone + /// TIFF file. + + bool ParseExtended (dng_stream &stream); + + /// Convert from a three-color to a four-color Bayer profile. + + virtual void SetFourColorBayer (); + + /// Find the hue/sat table to use for a given white point, if any. + /// The calling routine owns the resulting table. + + dng_hue_sat_map * HueSatMapForWhite (const dng_xy_coord &white) const; + + /// Stub out the profile (free memory used by large tables). + + void Stub (); + + /// Was this profile stubbed? + + bool WasStubbed () const + { + return fWasStubbed; + } + + protected: + + static real64 IlluminantToTemperature (uint32 light); + + void ClearFingerprint () + { + fFingerprint.Clear (); + } + + void CalculateFingerprint () const; + + static bool ValidForwardMatrix (const dng_matrix &m); + + static void ReadHueSatMap (dng_stream &stream, + dng_hue_sat_map &hueSatMap, + uint32 hues, + uint32 sats, + uint32 vals, + bool skipSat0); + + }; + +/******************************************************************************/ + +void SplitCameraProfileName (const dng_string &name, + dng_string &baseName, + int32 &version); + +/*****************************************************************************/ + +void BuildHueSatMapEncodingTable (dng_memory_allocator &allocator, + uint32 encoding, + AutoPtr &encodeTable, + AutoPtr &decodeTable, + bool subSample); + +/******************************************************************************/ + +#endif + +/******************************************************************************/ diff --git a/source/lib/dng_sdk/dng_classes.h b/source/lib/dng_sdk/dng_classes.h new file mode 100644 index 0000000..be72c4a --- /dev/null +++ b/source/lib/dng_sdk/dng_classes.h @@ -0,0 +1,100 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_classes.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*** \file + * Forward class declarations to avoid having to include many .h files in most places. + */ + +/*****************************************************************************/ + +#ifndef __dng_classes__ +#define __dng_classes__ + +/*****************************************************************************/ + +class dng_1d_function; +class dng_1d_table; +class dng_abort_sniffer; +class dng_area_task; +class dng_basic_tag_set; +class dng_camera_profile; +class dng_camera_profile_id; +class dng_camera_profile_info; +class dng_color_space; +class dng_color_spec; +class dng_date_time; +class dng_date_time_info; +class dng_exif; +class dng_fingerprint; +class dng_host; +class dng_hue_sat_map; +class dng_ifd; +class dng_image; +class dng_image_preview; +class dng_image_writer; +class dng_info; +class dng_iptc; +class dng_jpeg_image; +class dng_jpeg_preview; +class dng_linearization_info; +class dng_matrix; +class dng_matrix_3by3; +class dng_matrix_4by3; +class dng_md5_printer; +class dng_memory_allocator; +class dng_memory_block; +class dng_memory_data; +class dng_memory_stream; +class dng_metadata; +class dng_mosaic_info; +class dng_mutex; +class dng_noise_function; +class dng_noise_profile; +class dng_opcode; +class dng_opcode_list; +class dng_orientation; +class dng_negative; +class dng_pixel_buffer; +class dng_point; +class dng_point_real64; +class dng_preview; +class dng_preview_info; +class dng_preview_list; +class dng_raw_preview; +class dng_read_image; +class dng_rect; +class dng_rect_real64; +class dng_render; +class dng_resolution; +class dng_shared; +class dng_spline_solver; +class dng_srational; +class dng_stream; +class dng_string; +class dng_string_list; +class dng_tiff_directory; +class dng_tile_buffer; +class dng_time_zone; +class dng_tone_curve; +class dng_urational; +class dng_vector; +class dng_vector_3; +class dng_xmp; +class dng_xmp_sdk; +class dng_xy_coord; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_color_space.cpp b/source/lib/dng_sdk/dng_color_space.cpp new file mode 100644 index 0000000..72d465f --- /dev/null +++ b/source/lib/dng_sdk/dng_color_space.cpp @@ -0,0 +1,1072 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_color_space.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +#include "dng_color_space.h" + +#include "dng_1d_table.h" +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_matrix.h" +#include "dng_spline.h" +#include "dng_utils.h" +#include "dng_xy_coord.h" + +/*****************************************************************************/ + +real64 dng_function_GammaEncode_sRGB::Evaluate (real64 x) const + { + + if (x <= 0.0031308) + return x * 12.92; + + else + return 1.055 * pow (x, 1.0 / 2.4) - 0.055; + + } + +/*****************************************************************************/ + +real64 dng_function_GammaEncode_sRGB::EvaluateInverse (real64 y) const + { + + if (y <= 0.0031308 * 12.92) + return y * (1.0 / 12.92); + + else + return pow ((y + 0.055) * (1.0 / 1.055), 2.4); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_function_GammaEncode_sRGB::Get () + { + + static dng_function_GammaEncode_sRGB static_function; + + return static_function; + + } + +/*****************************************************************************/ + +real64 dng_function_GammaEncode_1_8::Evaluate (real64 x) const + { + + const real64 gamma = 1.0 / 1.8; + + const real64 slope0 = 32.0; + + const real64 x1 = 8.2118790552e-4; // pow (slope0, 1.0 / (gamma - 1.0)) * 2.0 + + const real64 y1 = 0.019310851; // pow (x1, gamma) + + const real64 slope1 = 13.064306598; // gamma * pow (x1, gamma - 1.0) + + if (x <= x1) + return EvaluateSplineSegment (x, + 0.0, + 0.0, + slope0, + x1, + y1, + slope1); + + else + return pow (x, gamma); + + } + +/*****************************************************************************/ + +real64 dng_function_GammaEncode_1_8::EvaluateInverse (real64 y) const + { + + if (y > 0.0 && y < 0.019310851) + { + + return dng_1d_function::EvaluateInverse (y); + + } + + return pow (y, 1.8); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_function_GammaEncode_1_8::Get () + { + + static dng_function_GammaEncode_1_8 static_function; + + return static_function; + + } + +/*****************************************************************************/ + +real64 dng_function_GammaEncode_2_2::Evaluate (real64 x) const + { + + const real64 gamma = 1.0 / 2.2; + + const real64 slope0 = 32.0; + + const real64 x1 = 0.0034800731; // pow (slope0, 1.0 / (gamma - 1.0)) * 2.0 + + const real64 y1 = 0.0763027458; // pow (x1, gamma) + + const real64 slope1 = 9.9661890075; // gamma * pow (x1, gamma - 1.0) + + if (x <= x1) + return EvaluateSplineSegment (x, + 0.0, + 0.0, + slope0, + x1, + y1, + slope1); + + else + return pow (x, gamma); + + } + +/*****************************************************************************/ + +real64 dng_function_GammaEncode_2_2::EvaluateInverse (real64 y) const + { + + if (y > 0.0 && y < 0.0763027458) + { + + return dng_1d_function::EvaluateInverse (y); + + } + + return pow (y, 2.2); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_function_GammaEncode_2_2::Get () + { + + static dng_function_GammaEncode_2_2 static_function; + + return static_function; + + } + +/*****************************************************************************/ + +dng_color_space::dng_color_space () + + : fMatrixToPCS () + , fMatrixFromPCS () + + { + + } + +/*****************************************************************************/ + +dng_color_space::~dng_color_space () + { + + } + +/*****************************************************************************/ + +void dng_color_space::SetMonochrome () + { + + fMatrixToPCS = PCStoXYZ ().AsColumn (); + + dng_matrix m (1, 3); + + m [0] [0] = 0.0; + m [0] [1] = 1.0; + m [0] [2] = 0.0; + + fMatrixFromPCS = m; + + } + +/*****************************************************************************/ + +void dng_color_space::SetMatrixToPCS (const dng_matrix_3by3 &M) + { + + // The matrix values are often rounded, so adjust to + // get them to convert device white exactly to the PCS. + + dng_vector_3 W1 = M * dng_vector_3 (1.0, 1.0, 1.0); + dng_vector_3 W2 = PCStoXYZ (); + + real64 s0 = W2 [0] / W1 [0]; + real64 s1 = W2 [1] / W1 [1]; + real64 s2 = W2 [2] / W1 [2]; + + dng_matrix_3by3 S (s0, 0, 0, + 0, s1, 0, + 0, 0, s2); + + fMatrixToPCS = S * M; + + // Find reverse matrix. + + fMatrixFromPCS = Invert (fMatrixToPCS); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_color_space::GammaFunction () const + { + + return dng_1d_identity::Get (); + + } + +/*****************************************************************************/ + +bool dng_color_space::ICCProfile (uint32 &size, + const uint8 *&data) const + { + + size = 0; + data = NULL; + + return false; + + } + +/*****************************************************************************/ + +dng_space_sRGB::dng_space_sRGB () + { + + SetMatrixToPCS (dng_matrix_3by3 (0.4361, 0.3851, 0.1431, + 0.2225, 0.7169, 0.0606, + 0.0139, 0.0971, 0.7141)); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_space_sRGB::GammaFunction () const + { + + return dng_function_GammaEncode_sRGB::Get (); + + } + +/*****************************************************************************/ + +bool dng_space_sRGB::ICCProfile (uint32 &size, + const uint8 *&data) const + + { + + static const uint8 ksRGBProfileData [] = + { + 0x00, 0x00, 0x0C, 0x48, 0x4C, 0x69, 0x6E, 0x6F, 0x02, 0x10, 0x00, 0x00, + 0x6D, 0x6E, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5A, 0x20, + 0x07, 0xCE, 0x00, 0x02, 0x00, 0x09, 0x00, 0x06, 0x00, 0x31, 0x00, 0x00, + 0x61, 0x63, 0x73, 0x70, 0x4D, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00, + 0x49, 0x45, 0x43, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2D, 0x48, 0x50, 0x20, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0x50, 0x00, 0x00, 0x00, 0x33, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x84, 0x00, 0x00, 0x00, 0x6C, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0xF0, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x6B, 0x70, 0x74, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x14, + 0x67, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x14, + 0x64, 0x6D, 0x6E, 0x64, 0x00, 0x00, 0x02, 0x54, 0x00, 0x00, 0x00, 0x70, + 0x64, 0x6D, 0x64, 0x64, 0x00, 0x00, 0x02, 0xC4, 0x00, 0x00, 0x00, 0x88, + 0x76, 0x75, 0x65, 0x64, 0x00, 0x00, 0x03, 0x4C, 0x00, 0x00, 0x00, 0x86, + 0x76, 0x69, 0x65, 0x77, 0x00, 0x00, 0x03, 0xD4, 0x00, 0x00, 0x00, 0x24, + 0x6C, 0x75, 0x6D, 0x69, 0x00, 0x00, 0x03, 0xF8, 0x00, 0x00, 0x00, 0x14, + 0x6D, 0x65, 0x61, 0x73, 0x00, 0x00, 0x04, 0x0C, 0x00, 0x00, 0x00, 0x24, + 0x74, 0x65, 0x63, 0x68, 0x00, 0x00, 0x04, 0x30, 0x00, 0x00, 0x00, 0x0C, + 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x08, 0x0C, + 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x08, 0x0C, + 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x08, 0x0C, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x31, 0x39, + 0x39, 0x38, 0x20, 0x48, 0x65, 0x77, 0x6C, 0x65, 0x74, 0x74, 0x2D, 0x50, + 0x61, 0x63, 0x6B, 0x61, 0x72, 0x64, 0x20, 0x43, 0x6F, 0x6D, 0x70, 0x61, + 0x6E, 0x79, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x12, 0x73, 0x52, 0x47, 0x42, 0x20, 0x49, 0x45, 0x43, + 0x36, 0x31, 0x39, 0x36, 0x36, 0x2D, 0x32, 0x2E, 0x31, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x73, 0x52, 0x47, + 0x42, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2D, 0x32, + 0x2E, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF3, 0x51, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xCC, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6F, 0xA2, 0x00, 0x00, 0x38, 0xF5, + 0x00, 0x00, 0x03, 0x90, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x62, 0x99, 0x00, 0x00, 0xB7, 0x85, 0x00, 0x00, 0x18, 0xDA, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x24, 0xA0, + 0x00, 0x00, 0x0F, 0x84, 0x00, 0x00, 0xB6, 0xCF, 0x64, 0x65, 0x73, 0x63, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, + 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x69, + 0x65, 0x63, 0x2E, 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x16, 0x49, 0x45, 0x43, 0x20, 0x68, 0x74, 0x74, + 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x77, 0x77, 0x2E, 0x69, 0x65, 0x63, 0x2E, + 0x63, 0x68, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, + 0x49, 0x45, 0x43, 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2D, 0x32, 0x2E, + 0x31, 0x20, 0x44, 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x52, 0x47, + 0x42, 0x20, 0x63, 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, + 0x63, 0x65, 0x20, 0x2D, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2E, 0x49, 0x45, 0x43, + 0x20, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2D, 0x32, 0x2E, 0x31, 0x20, 0x44, + 0x65, 0x66, 0x61, 0x75, 0x6C, 0x74, 0x20, 0x52, 0x47, 0x42, 0x20, 0x63, + 0x6F, 0x6C, 0x6F, 0x75, 0x72, 0x20, 0x73, 0x70, 0x61, 0x63, 0x65, 0x20, + 0x2D, 0x20, 0x73, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x2C, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6E, 0x63, + 0x65, 0x20, 0x56, 0x69, 0x65, 0x77, 0x69, 0x6E, 0x67, 0x20, 0x43, 0x6F, + 0x6E, 0x64, 0x69, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x49, + 0x45, 0x43, 0x36, 0x31, 0x39, 0x36, 0x36, 0x2D, 0x32, 0x2E, 0x31, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2C, 0x52, + 0x65, 0x66, 0x65, 0x72, 0x65, 0x6E, 0x63, 0x65, 0x20, 0x56, 0x69, 0x65, + 0x77, 0x69, 0x6E, 0x67, 0x20, 0x43, 0x6F, 0x6E, 0x64, 0x69, 0x74, 0x69, + 0x6F, 0x6E, 0x20, 0x69, 0x6E, 0x20, 0x49, 0x45, 0x43, 0x36, 0x31, 0x39, + 0x36, 0x36, 0x2D, 0x32, 0x2E, 0x31, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x76, 0x69, 0x65, 0x77, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x13, 0xA4, 0xFE, 0x00, 0x14, 0x5F, 0x2E, + 0x00, 0x10, 0xCF, 0x14, 0x00, 0x03, 0xED, 0xCC, 0x00, 0x04, 0x13, 0x0B, + 0x00, 0x03, 0x5C, 0x9E, 0x00, 0x00, 0x00, 0x01, 0x58, 0x59, 0x5A, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x09, 0x56, 0x00, 0x50, 0x00, 0x00, + 0x00, 0x57, 0x1F, 0xE7, 0x6D, 0x65, 0x61, 0x73, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x8F, + 0x00, 0x00, 0x00, 0x02, 0x73, 0x69, 0x67, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x43, 0x52, 0x54, 0x20, 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x0A, 0x00, 0x0F, + 0x00, 0x14, 0x00, 0x19, 0x00, 0x1E, 0x00, 0x23, 0x00, 0x28, 0x00, 0x2D, + 0x00, 0x32, 0x00, 0x37, 0x00, 0x3B, 0x00, 0x40, 0x00, 0x45, 0x00, 0x4A, + 0x00, 0x4F, 0x00, 0x54, 0x00, 0x59, 0x00, 0x5E, 0x00, 0x63, 0x00, 0x68, + 0x00, 0x6D, 0x00, 0x72, 0x00, 0x77, 0x00, 0x7C, 0x00, 0x81, 0x00, 0x86, + 0x00, 0x8B, 0x00, 0x90, 0x00, 0x95, 0x00, 0x9A, 0x00, 0x9F, 0x00, 0xA4, + 0x00, 0xA9, 0x00, 0xAE, 0x00, 0xB2, 0x00, 0xB7, 0x00, 0xBC, 0x00, 0xC1, + 0x00, 0xC6, 0x00, 0xCB, 0x00, 0xD0, 0x00, 0xD5, 0x00, 0xDB, 0x00, 0xE0, + 0x00, 0xE5, 0x00, 0xEB, 0x00, 0xF0, 0x00, 0xF6, 0x00, 0xFB, 0x01, 0x01, + 0x01, 0x07, 0x01, 0x0D, 0x01, 0x13, 0x01, 0x19, 0x01, 0x1F, 0x01, 0x25, + 0x01, 0x2B, 0x01, 0x32, 0x01, 0x38, 0x01, 0x3E, 0x01, 0x45, 0x01, 0x4C, + 0x01, 0x52, 0x01, 0x59, 0x01, 0x60, 0x01, 0x67, 0x01, 0x6E, 0x01, 0x75, + 0x01, 0x7C, 0x01, 0x83, 0x01, 0x8B, 0x01, 0x92, 0x01, 0x9A, 0x01, 0xA1, + 0x01, 0xA9, 0x01, 0xB1, 0x01, 0xB9, 0x01, 0xC1, 0x01, 0xC9, 0x01, 0xD1, + 0x01, 0xD9, 0x01, 0xE1, 0x01, 0xE9, 0x01, 0xF2, 0x01, 0xFA, 0x02, 0x03, + 0x02, 0x0C, 0x02, 0x14, 0x02, 0x1D, 0x02, 0x26, 0x02, 0x2F, 0x02, 0x38, + 0x02, 0x41, 0x02, 0x4B, 0x02, 0x54, 0x02, 0x5D, 0x02, 0x67, 0x02, 0x71, + 0x02, 0x7A, 0x02, 0x84, 0x02, 0x8E, 0x02, 0x98, 0x02, 0xA2, 0x02, 0xAC, + 0x02, 0xB6, 0x02, 0xC1, 0x02, 0xCB, 0x02, 0xD5, 0x02, 0xE0, 0x02, 0xEB, + 0x02, 0xF5, 0x03, 0x00, 0x03, 0x0B, 0x03, 0x16, 0x03, 0x21, 0x03, 0x2D, + 0x03, 0x38, 0x03, 0x43, 0x03, 0x4F, 0x03, 0x5A, 0x03, 0x66, 0x03, 0x72, + 0x03, 0x7E, 0x03, 0x8A, 0x03, 0x96, 0x03, 0xA2, 0x03, 0xAE, 0x03, 0xBA, + 0x03, 0xC7, 0x03, 0xD3, 0x03, 0xE0, 0x03, 0xEC, 0x03, 0xF9, 0x04, 0x06, + 0x04, 0x13, 0x04, 0x20, 0x04, 0x2D, 0x04, 0x3B, 0x04, 0x48, 0x04, 0x55, + 0x04, 0x63, 0x04, 0x71, 0x04, 0x7E, 0x04, 0x8C, 0x04, 0x9A, 0x04, 0xA8, + 0x04, 0xB6, 0x04, 0xC4, 0x04, 0xD3, 0x04, 0xE1, 0x04, 0xF0, 0x04, 0xFE, + 0x05, 0x0D, 0x05, 0x1C, 0x05, 0x2B, 0x05, 0x3A, 0x05, 0x49, 0x05, 0x58, + 0x05, 0x67, 0x05, 0x77, 0x05, 0x86, 0x05, 0x96, 0x05, 0xA6, 0x05, 0xB5, + 0x05, 0xC5, 0x05, 0xD5, 0x05, 0xE5, 0x05, 0xF6, 0x06, 0x06, 0x06, 0x16, + 0x06, 0x27, 0x06, 0x37, 0x06, 0x48, 0x06, 0x59, 0x06, 0x6A, 0x06, 0x7B, + 0x06, 0x8C, 0x06, 0x9D, 0x06, 0xAF, 0x06, 0xC0, 0x06, 0xD1, 0x06, 0xE3, + 0x06, 0xF5, 0x07, 0x07, 0x07, 0x19, 0x07, 0x2B, 0x07, 0x3D, 0x07, 0x4F, + 0x07, 0x61, 0x07, 0x74, 0x07, 0x86, 0x07, 0x99, 0x07, 0xAC, 0x07, 0xBF, + 0x07, 0xD2, 0x07, 0xE5, 0x07, 0xF8, 0x08, 0x0B, 0x08, 0x1F, 0x08, 0x32, + 0x08, 0x46, 0x08, 0x5A, 0x08, 0x6E, 0x08, 0x82, 0x08, 0x96, 0x08, 0xAA, + 0x08, 0xBE, 0x08, 0xD2, 0x08, 0xE7, 0x08, 0xFB, 0x09, 0x10, 0x09, 0x25, + 0x09, 0x3A, 0x09, 0x4F, 0x09, 0x64, 0x09, 0x79, 0x09, 0x8F, 0x09, 0xA4, + 0x09, 0xBA, 0x09, 0xCF, 0x09, 0xE5, 0x09, 0xFB, 0x0A, 0x11, 0x0A, 0x27, + 0x0A, 0x3D, 0x0A, 0x54, 0x0A, 0x6A, 0x0A, 0x81, 0x0A, 0x98, 0x0A, 0xAE, + 0x0A, 0xC5, 0x0A, 0xDC, 0x0A, 0xF3, 0x0B, 0x0B, 0x0B, 0x22, 0x0B, 0x39, + 0x0B, 0x51, 0x0B, 0x69, 0x0B, 0x80, 0x0B, 0x98, 0x0B, 0xB0, 0x0B, 0xC8, + 0x0B, 0xE1, 0x0B, 0xF9, 0x0C, 0x12, 0x0C, 0x2A, 0x0C, 0x43, 0x0C, 0x5C, + 0x0C, 0x75, 0x0C, 0x8E, 0x0C, 0xA7, 0x0C, 0xC0, 0x0C, 0xD9, 0x0C, 0xF3, + 0x0D, 0x0D, 0x0D, 0x26, 0x0D, 0x40, 0x0D, 0x5A, 0x0D, 0x74, 0x0D, 0x8E, + 0x0D, 0xA9, 0x0D, 0xC3, 0x0D, 0xDE, 0x0D, 0xF8, 0x0E, 0x13, 0x0E, 0x2E, + 0x0E, 0x49, 0x0E, 0x64, 0x0E, 0x7F, 0x0E, 0x9B, 0x0E, 0xB6, 0x0E, 0xD2, + 0x0E, 0xEE, 0x0F, 0x09, 0x0F, 0x25, 0x0F, 0x41, 0x0F, 0x5E, 0x0F, 0x7A, + 0x0F, 0x96, 0x0F, 0xB3, 0x0F, 0xCF, 0x0F, 0xEC, 0x10, 0x09, 0x10, 0x26, + 0x10, 0x43, 0x10, 0x61, 0x10, 0x7E, 0x10, 0x9B, 0x10, 0xB9, 0x10, 0xD7, + 0x10, 0xF5, 0x11, 0x13, 0x11, 0x31, 0x11, 0x4F, 0x11, 0x6D, 0x11, 0x8C, + 0x11, 0xAA, 0x11, 0xC9, 0x11, 0xE8, 0x12, 0x07, 0x12, 0x26, 0x12, 0x45, + 0x12, 0x64, 0x12, 0x84, 0x12, 0xA3, 0x12, 0xC3, 0x12, 0xE3, 0x13, 0x03, + 0x13, 0x23, 0x13, 0x43, 0x13, 0x63, 0x13, 0x83, 0x13, 0xA4, 0x13, 0xC5, + 0x13, 0xE5, 0x14, 0x06, 0x14, 0x27, 0x14, 0x49, 0x14, 0x6A, 0x14, 0x8B, + 0x14, 0xAD, 0x14, 0xCE, 0x14, 0xF0, 0x15, 0x12, 0x15, 0x34, 0x15, 0x56, + 0x15, 0x78, 0x15, 0x9B, 0x15, 0xBD, 0x15, 0xE0, 0x16, 0x03, 0x16, 0x26, + 0x16, 0x49, 0x16, 0x6C, 0x16, 0x8F, 0x16, 0xB2, 0x16, 0xD6, 0x16, 0xFA, + 0x17, 0x1D, 0x17, 0x41, 0x17, 0x65, 0x17, 0x89, 0x17, 0xAE, 0x17, 0xD2, + 0x17, 0xF7, 0x18, 0x1B, 0x18, 0x40, 0x18, 0x65, 0x18, 0x8A, 0x18, 0xAF, + 0x18, 0xD5, 0x18, 0xFA, 0x19, 0x20, 0x19, 0x45, 0x19, 0x6B, 0x19, 0x91, + 0x19, 0xB7, 0x19, 0xDD, 0x1A, 0x04, 0x1A, 0x2A, 0x1A, 0x51, 0x1A, 0x77, + 0x1A, 0x9E, 0x1A, 0xC5, 0x1A, 0xEC, 0x1B, 0x14, 0x1B, 0x3B, 0x1B, 0x63, + 0x1B, 0x8A, 0x1B, 0xB2, 0x1B, 0xDA, 0x1C, 0x02, 0x1C, 0x2A, 0x1C, 0x52, + 0x1C, 0x7B, 0x1C, 0xA3, 0x1C, 0xCC, 0x1C, 0xF5, 0x1D, 0x1E, 0x1D, 0x47, + 0x1D, 0x70, 0x1D, 0x99, 0x1D, 0xC3, 0x1D, 0xEC, 0x1E, 0x16, 0x1E, 0x40, + 0x1E, 0x6A, 0x1E, 0x94, 0x1E, 0xBE, 0x1E, 0xE9, 0x1F, 0x13, 0x1F, 0x3E, + 0x1F, 0x69, 0x1F, 0x94, 0x1F, 0xBF, 0x1F, 0xEA, 0x20, 0x15, 0x20, 0x41, + 0x20, 0x6C, 0x20, 0x98, 0x20, 0xC4, 0x20, 0xF0, 0x21, 0x1C, 0x21, 0x48, + 0x21, 0x75, 0x21, 0xA1, 0x21, 0xCE, 0x21, 0xFB, 0x22, 0x27, 0x22, 0x55, + 0x22, 0x82, 0x22, 0xAF, 0x22, 0xDD, 0x23, 0x0A, 0x23, 0x38, 0x23, 0x66, + 0x23, 0x94, 0x23, 0xC2, 0x23, 0xF0, 0x24, 0x1F, 0x24, 0x4D, 0x24, 0x7C, + 0x24, 0xAB, 0x24, 0xDA, 0x25, 0x09, 0x25, 0x38, 0x25, 0x68, 0x25, 0x97, + 0x25, 0xC7, 0x25, 0xF7, 0x26, 0x27, 0x26, 0x57, 0x26, 0x87, 0x26, 0xB7, + 0x26, 0xE8, 0x27, 0x18, 0x27, 0x49, 0x27, 0x7A, 0x27, 0xAB, 0x27, 0xDC, + 0x28, 0x0D, 0x28, 0x3F, 0x28, 0x71, 0x28, 0xA2, 0x28, 0xD4, 0x29, 0x06, + 0x29, 0x38, 0x29, 0x6B, 0x29, 0x9D, 0x29, 0xD0, 0x2A, 0x02, 0x2A, 0x35, + 0x2A, 0x68, 0x2A, 0x9B, 0x2A, 0xCF, 0x2B, 0x02, 0x2B, 0x36, 0x2B, 0x69, + 0x2B, 0x9D, 0x2B, 0xD1, 0x2C, 0x05, 0x2C, 0x39, 0x2C, 0x6E, 0x2C, 0xA2, + 0x2C, 0xD7, 0x2D, 0x0C, 0x2D, 0x41, 0x2D, 0x76, 0x2D, 0xAB, 0x2D, 0xE1, + 0x2E, 0x16, 0x2E, 0x4C, 0x2E, 0x82, 0x2E, 0xB7, 0x2E, 0xEE, 0x2F, 0x24, + 0x2F, 0x5A, 0x2F, 0x91, 0x2F, 0xC7, 0x2F, 0xFE, 0x30, 0x35, 0x30, 0x6C, + 0x30, 0xA4, 0x30, 0xDB, 0x31, 0x12, 0x31, 0x4A, 0x31, 0x82, 0x31, 0xBA, + 0x31, 0xF2, 0x32, 0x2A, 0x32, 0x63, 0x32, 0x9B, 0x32, 0xD4, 0x33, 0x0D, + 0x33, 0x46, 0x33, 0x7F, 0x33, 0xB8, 0x33, 0xF1, 0x34, 0x2B, 0x34, 0x65, + 0x34, 0x9E, 0x34, 0xD8, 0x35, 0x13, 0x35, 0x4D, 0x35, 0x87, 0x35, 0xC2, + 0x35, 0xFD, 0x36, 0x37, 0x36, 0x72, 0x36, 0xAE, 0x36, 0xE9, 0x37, 0x24, + 0x37, 0x60, 0x37, 0x9C, 0x37, 0xD7, 0x38, 0x14, 0x38, 0x50, 0x38, 0x8C, + 0x38, 0xC8, 0x39, 0x05, 0x39, 0x42, 0x39, 0x7F, 0x39, 0xBC, 0x39, 0xF9, + 0x3A, 0x36, 0x3A, 0x74, 0x3A, 0xB2, 0x3A, 0xEF, 0x3B, 0x2D, 0x3B, 0x6B, + 0x3B, 0xAA, 0x3B, 0xE8, 0x3C, 0x27, 0x3C, 0x65, 0x3C, 0xA4, 0x3C, 0xE3, + 0x3D, 0x22, 0x3D, 0x61, 0x3D, 0xA1, 0x3D, 0xE0, 0x3E, 0x20, 0x3E, 0x60, + 0x3E, 0xA0, 0x3E, 0xE0, 0x3F, 0x21, 0x3F, 0x61, 0x3F, 0xA2, 0x3F, 0xE2, + 0x40, 0x23, 0x40, 0x64, 0x40, 0xA6, 0x40, 0xE7, 0x41, 0x29, 0x41, 0x6A, + 0x41, 0xAC, 0x41, 0xEE, 0x42, 0x30, 0x42, 0x72, 0x42, 0xB5, 0x42, 0xF7, + 0x43, 0x3A, 0x43, 0x7D, 0x43, 0xC0, 0x44, 0x03, 0x44, 0x47, 0x44, 0x8A, + 0x44, 0xCE, 0x45, 0x12, 0x45, 0x55, 0x45, 0x9A, 0x45, 0xDE, 0x46, 0x22, + 0x46, 0x67, 0x46, 0xAB, 0x46, 0xF0, 0x47, 0x35, 0x47, 0x7B, 0x47, 0xC0, + 0x48, 0x05, 0x48, 0x4B, 0x48, 0x91, 0x48, 0xD7, 0x49, 0x1D, 0x49, 0x63, + 0x49, 0xA9, 0x49, 0xF0, 0x4A, 0x37, 0x4A, 0x7D, 0x4A, 0xC4, 0x4B, 0x0C, + 0x4B, 0x53, 0x4B, 0x9A, 0x4B, 0xE2, 0x4C, 0x2A, 0x4C, 0x72, 0x4C, 0xBA, + 0x4D, 0x02, 0x4D, 0x4A, 0x4D, 0x93, 0x4D, 0xDC, 0x4E, 0x25, 0x4E, 0x6E, + 0x4E, 0xB7, 0x4F, 0x00, 0x4F, 0x49, 0x4F, 0x93, 0x4F, 0xDD, 0x50, 0x27, + 0x50, 0x71, 0x50, 0xBB, 0x51, 0x06, 0x51, 0x50, 0x51, 0x9B, 0x51, 0xE6, + 0x52, 0x31, 0x52, 0x7C, 0x52, 0xC7, 0x53, 0x13, 0x53, 0x5F, 0x53, 0xAA, + 0x53, 0xF6, 0x54, 0x42, 0x54, 0x8F, 0x54, 0xDB, 0x55, 0x28, 0x55, 0x75, + 0x55, 0xC2, 0x56, 0x0F, 0x56, 0x5C, 0x56, 0xA9, 0x56, 0xF7, 0x57, 0x44, + 0x57, 0x92, 0x57, 0xE0, 0x58, 0x2F, 0x58, 0x7D, 0x58, 0xCB, 0x59, 0x1A, + 0x59, 0x69, 0x59, 0xB8, 0x5A, 0x07, 0x5A, 0x56, 0x5A, 0xA6, 0x5A, 0xF5, + 0x5B, 0x45, 0x5B, 0x95, 0x5B, 0xE5, 0x5C, 0x35, 0x5C, 0x86, 0x5C, 0xD6, + 0x5D, 0x27, 0x5D, 0x78, 0x5D, 0xC9, 0x5E, 0x1A, 0x5E, 0x6C, 0x5E, 0xBD, + 0x5F, 0x0F, 0x5F, 0x61, 0x5F, 0xB3, 0x60, 0x05, 0x60, 0x57, 0x60, 0xAA, + 0x60, 0xFC, 0x61, 0x4F, 0x61, 0xA2, 0x61, 0xF5, 0x62, 0x49, 0x62, 0x9C, + 0x62, 0xF0, 0x63, 0x43, 0x63, 0x97, 0x63, 0xEB, 0x64, 0x40, 0x64, 0x94, + 0x64, 0xE9, 0x65, 0x3D, 0x65, 0x92, 0x65, 0xE7, 0x66, 0x3D, 0x66, 0x92, + 0x66, 0xE8, 0x67, 0x3D, 0x67, 0x93, 0x67, 0xE9, 0x68, 0x3F, 0x68, 0x96, + 0x68, 0xEC, 0x69, 0x43, 0x69, 0x9A, 0x69, 0xF1, 0x6A, 0x48, 0x6A, 0x9F, + 0x6A, 0xF7, 0x6B, 0x4F, 0x6B, 0xA7, 0x6B, 0xFF, 0x6C, 0x57, 0x6C, 0xAF, + 0x6D, 0x08, 0x6D, 0x60, 0x6D, 0xB9, 0x6E, 0x12, 0x6E, 0x6B, 0x6E, 0xC4, + 0x6F, 0x1E, 0x6F, 0x78, 0x6F, 0xD1, 0x70, 0x2B, 0x70, 0x86, 0x70, 0xE0, + 0x71, 0x3A, 0x71, 0x95, 0x71, 0xF0, 0x72, 0x4B, 0x72, 0xA6, 0x73, 0x01, + 0x73, 0x5D, 0x73, 0xB8, 0x74, 0x14, 0x74, 0x70, 0x74, 0xCC, 0x75, 0x28, + 0x75, 0x85, 0x75, 0xE1, 0x76, 0x3E, 0x76, 0x9B, 0x76, 0xF8, 0x77, 0x56, + 0x77, 0xB3, 0x78, 0x11, 0x78, 0x6E, 0x78, 0xCC, 0x79, 0x2A, 0x79, 0x89, + 0x79, 0xE7, 0x7A, 0x46, 0x7A, 0xA5, 0x7B, 0x04, 0x7B, 0x63, 0x7B, 0xC2, + 0x7C, 0x21, 0x7C, 0x81, 0x7C, 0xE1, 0x7D, 0x41, 0x7D, 0xA1, 0x7E, 0x01, + 0x7E, 0x62, 0x7E, 0xC2, 0x7F, 0x23, 0x7F, 0x84, 0x7F, 0xE5, 0x80, 0x47, + 0x80, 0xA8, 0x81, 0x0A, 0x81, 0x6B, 0x81, 0xCD, 0x82, 0x30, 0x82, 0x92, + 0x82, 0xF4, 0x83, 0x57, 0x83, 0xBA, 0x84, 0x1D, 0x84, 0x80, 0x84, 0xE3, + 0x85, 0x47, 0x85, 0xAB, 0x86, 0x0E, 0x86, 0x72, 0x86, 0xD7, 0x87, 0x3B, + 0x87, 0x9F, 0x88, 0x04, 0x88, 0x69, 0x88, 0xCE, 0x89, 0x33, 0x89, 0x99, + 0x89, 0xFE, 0x8A, 0x64, 0x8A, 0xCA, 0x8B, 0x30, 0x8B, 0x96, 0x8B, 0xFC, + 0x8C, 0x63, 0x8C, 0xCA, 0x8D, 0x31, 0x8D, 0x98, 0x8D, 0xFF, 0x8E, 0x66, + 0x8E, 0xCE, 0x8F, 0x36, 0x8F, 0x9E, 0x90, 0x06, 0x90, 0x6E, 0x90, 0xD6, + 0x91, 0x3F, 0x91, 0xA8, 0x92, 0x11, 0x92, 0x7A, 0x92, 0xE3, 0x93, 0x4D, + 0x93, 0xB6, 0x94, 0x20, 0x94, 0x8A, 0x94, 0xF4, 0x95, 0x5F, 0x95, 0xC9, + 0x96, 0x34, 0x96, 0x9F, 0x97, 0x0A, 0x97, 0x75, 0x97, 0xE0, 0x98, 0x4C, + 0x98, 0xB8, 0x99, 0x24, 0x99, 0x90, 0x99, 0xFC, 0x9A, 0x68, 0x9A, 0xD5, + 0x9B, 0x42, 0x9B, 0xAF, 0x9C, 0x1C, 0x9C, 0x89, 0x9C, 0xF7, 0x9D, 0x64, + 0x9D, 0xD2, 0x9E, 0x40, 0x9E, 0xAE, 0x9F, 0x1D, 0x9F, 0x8B, 0x9F, 0xFA, + 0xA0, 0x69, 0xA0, 0xD8, 0xA1, 0x47, 0xA1, 0xB6, 0xA2, 0x26, 0xA2, 0x96, + 0xA3, 0x06, 0xA3, 0x76, 0xA3, 0xE6, 0xA4, 0x56, 0xA4, 0xC7, 0xA5, 0x38, + 0xA5, 0xA9, 0xA6, 0x1A, 0xA6, 0x8B, 0xA6, 0xFD, 0xA7, 0x6E, 0xA7, 0xE0, + 0xA8, 0x52, 0xA8, 0xC4, 0xA9, 0x37, 0xA9, 0xA9, 0xAA, 0x1C, 0xAA, 0x8F, + 0xAB, 0x02, 0xAB, 0x75, 0xAB, 0xE9, 0xAC, 0x5C, 0xAC, 0xD0, 0xAD, 0x44, + 0xAD, 0xB8, 0xAE, 0x2D, 0xAE, 0xA1, 0xAF, 0x16, 0xAF, 0x8B, 0xB0, 0x00, + 0xB0, 0x75, 0xB0, 0xEA, 0xB1, 0x60, 0xB1, 0xD6, 0xB2, 0x4B, 0xB2, 0xC2, + 0xB3, 0x38, 0xB3, 0xAE, 0xB4, 0x25, 0xB4, 0x9C, 0xB5, 0x13, 0xB5, 0x8A, + 0xB6, 0x01, 0xB6, 0x79, 0xB6, 0xF0, 0xB7, 0x68, 0xB7, 0xE0, 0xB8, 0x59, + 0xB8, 0xD1, 0xB9, 0x4A, 0xB9, 0xC2, 0xBA, 0x3B, 0xBA, 0xB5, 0xBB, 0x2E, + 0xBB, 0xA7, 0xBC, 0x21, 0xBC, 0x9B, 0xBD, 0x15, 0xBD, 0x8F, 0xBE, 0x0A, + 0xBE, 0x84, 0xBE, 0xFF, 0xBF, 0x7A, 0xBF, 0xF5, 0xC0, 0x70, 0xC0, 0xEC, + 0xC1, 0x67, 0xC1, 0xE3, 0xC2, 0x5F, 0xC2, 0xDB, 0xC3, 0x58, 0xC3, 0xD4, + 0xC4, 0x51, 0xC4, 0xCE, 0xC5, 0x4B, 0xC5, 0xC8, 0xC6, 0x46, 0xC6, 0xC3, + 0xC7, 0x41, 0xC7, 0xBF, 0xC8, 0x3D, 0xC8, 0xBC, 0xC9, 0x3A, 0xC9, 0xB9, + 0xCA, 0x38, 0xCA, 0xB7, 0xCB, 0x36, 0xCB, 0xB6, 0xCC, 0x35, 0xCC, 0xB5, + 0xCD, 0x35, 0xCD, 0xB5, 0xCE, 0x36, 0xCE, 0xB6, 0xCF, 0x37, 0xCF, 0xB8, + 0xD0, 0x39, 0xD0, 0xBA, 0xD1, 0x3C, 0xD1, 0xBE, 0xD2, 0x3F, 0xD2, 0xC1, + 0xD3, 0x44, 0xD3, 0xC6, 0xD4, 0x49, 0xD4, 0xCB, 0xD5, 0x4E, 0xD5, 0xD1, + 0xD6, 0x55, 0xD6, 0xD8, 0xD7, 0x5C, 0xD7, 0xE0, 0xD8, 0x64, 0xD8, 0xE8, + 0xD9, 0x6C, 0xD9, 0xF1, 0xDA, 0x76, 0xDA, 0xFB, 0xDB, 0x80, 0xDC, 0x05, + 0xDC, 0x8A, 0xDD, 0x10, 0xDD, 0x96, 0xDE, 0x1C, 0xDE, 0xA2, 0xDF, 0x29, + 0xDF, 0xAF, 0xE0, 0x36, 0xE0, 0xBD, 0xE1, 0x44, 0xE1, 0xCC, 0xE2, 0x53, + 0xE2, 0xDB, 0xE3, 0x63, 0xE3, 0xEB, 0xE4, 0x73, 0xE4, 0xFC, 0xE5, 0x84, + 0xE6, 0x0D, 0xE6, 0x96, 0xE7, 0x1F, 0xE7, 0xA9, 0xE8, 0x32, 0xE8, 0xBC, + 0xE9, 0x46, 0xE9, 0xD0, 0xEA, 0x5B, 0xEA, 0xE5, 0xEB, 0x70, 0xEB, 0xFB, + 0xEC, 0x86, 0xED, 0x11, 0xED, 0x9C, 0xEE, 0x28, 0xEE, 0xB4, 0xEF, 0x40, + 0xEF, 0xCC, 0xF0, 0x58, 0xF0, 0xE5, 0xF1, 0x72, 0xF1, 0xFF, 0xF2, 0x8C, + 0xF3, 0x19, 0xF3, 0xA7, 0xF4, 0x34, 0xF4, 0xC2, 0xF5, 0x50, 0xF5, 0xDE, + 0xF6, 0x6D, 0xF6, 0xFB, 0xF7, 0x8A, 0xF8, 0x19, 0xF8, 0xA8, 0xF9, 0x38, + 0xF9, 0xC7, 0xFA, 0x57, 0xFA, 0xE7, 0xFB, 0x77, 0xFC, 0x07, 0xFC, 0x98, + 0xFD, 0x29, 0xFD, 0xBA, 0xFE, 0x4B, 0xFE, 0xDC, 0xFF, 0x6D, 0xFF, 0xFF + }; + + size = sizeof (ksRGBProfileData); + data = ksRGBProfileData; + + return true; + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_sRGB::Get () + { + + static dng_space_sRGB static_space; + + return static_space; + + } + +/*****************************************************************************/ + +dng_space_AdobeRGB::dng_space_AdobeRGB () + { + + SetMatrixToPCS (dng_matrix_3by3 (0.6097, 0.2053, 0.1492, + 0.3111, 0.6257, 0.0632, + 0.0195, 0.0609, 0.7446)); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_space_AdobeRGB::GammaFunction () const + { + + return dng_function_GammaEncode_2_2::Get (); + + } + +/*****************************************************************************/ + +bool dng_space_AdobeRGB::ICCProfile (uint32 &size, + const uint8 *&data) const + + { + + static const uint8 kAdobeRGBProfileData [] = + { + 0x00, 0x00, 0x02, 0x30, 0x41, 0x44, 0x42, 0x45, 0x02, 0x10, 0x00, 0x00, + 0x6D, 0x6E, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5A, 0x20, + 0x07, 0xCF, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x63, 0x73, 0x70, 0x41, 0x50, 0x50, 0x4C, 0x00, 0x00, 0x00, 0x00, + 0x6E, 0x6F, 0x6E, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2D, 0x41, 0x44, 0x42, 0x45, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x32, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x00, 0x6B, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x9C, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x6B, 0x70, 0x74, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xC4, 0x00, 0x00, 0x00, 0x0E, + 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xD4, 0x00, 0x00, 0x00, 0x0E, + 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xE4, 0x00, 0x00, 0x00, 0x0E, + 0x72, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x14, + 0x67, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x00, 0x14, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x41, + 0x64, 0x6F, 0x62, 0x65, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x73, + 0x20, 0x49, 0x6E, 0x63, 0x6F, 0x72, 0x70, 0x6F, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x11, 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x52, 0x47, + 0x42, 0x20, 0x28, 0x31, 0x39, 0x39, 0x38, 0x29, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF3, 0x51, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xCC, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x33, 0x00, 0x00, + 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x02, 0x33, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x02, 0x33, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x9C, 0x18, 0x00, 0x00, 0x4F, 0xA5, + 0x00, 0x00, 0x04, 0xFC, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x34, 0x8D, 0x00, 0x00, 0xA0, 0x2C, 0x00, 0x00, 0x0F, 0x95, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x26, 0x31, + 0x00, 0x00, 0x10, 0x2F, 0x00, 0x00, 0xBE, 0x9C + }; + + size = sizeof (kAdobeRGBProfileData); + data = kAdobeRGBProfileData; + + return true; + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_AdobeRGB::Get () + { + + static dng_space_AdobeRGB static_space; + + return static_space; + + } + +/*****************************************************************************/ + +dng_space_ColorMatch::dng_space_ColorMatch () + { + + SetMatrixToPCS (dng_matrix_3by3 (0.5094, 0.3208, 0.1339, + 0.2749, 0.6581, 0.0670, + 0.0243, 0.1087, 0.6919)); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_space_ColorMatch::GammaFunction () const + { + + return dng_function_GammaEncode_1_8::Get (); + + } + +/*****************************************************************************/ + +bool dng_space_ColorMatch::ICCProfile (uint32 &size, + const uint8 *&data) const + + { + + static const uint8 kColorMatchProfileData [] = + { + 0x00, 0x00, 0x02, 0x30, 0x41, 0x44, 0x42, 0x45, 0x02, 0x10, 0x00, 0x00, + 0x6D, 0x6E, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5A, 0x20, + 0x07, 0xCF, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x63, 0x73, 0x70, 0x41, 0x50, 0x50, 0x4C, 0x00, 0x00, 0x00, 0x00, + 0x6E, 0x6F, 0x6E, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2D, 0x41, 0x44, 0x42, 0x45, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x00, 0xFC, 0x00, 0x00, 0x00, 0x32, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x30, 0x00, 0x00, 0x00, 0x69, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x9C, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x6B, 0x70, 0x74, 0x00, 0x00, 0x01, 0xB0, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xC4, 0x00, 0x00, 0x00, 0x0E, + 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xD4, 0x00, 0x00, 0x00, 0x0E, + 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xE4, 0x00, 0x00, 0x00, 0x0E, + 0x72, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x14, + 0x67, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x08, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x1C, 0x00, 0x00, 0x00, 0x14, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x41, + 0x64, 0x6F, 0x62, 0x65, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x73, + 0x20, 0x49, 0x6E, 0x63, 0x6F, 0x72, 0x70, 0x6F, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x43, 0x6F, 0x6C, 0x6F, 0x72, 0x4D, 0x61, 0x74, + 0x63, 0x68, 0x20, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF6, 0xDC, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x3A, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xCD, 0x00, 0x00, + 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, + 0x01, 0xCD, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x01, 0xCD, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x82, 0x6B, 0x00, 0x00, 0x46, 0x63, + 0x00, 0x00, 0x06, 0x36, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x52, 0x23, 0x00, 0x00, 0xA8, 0x79, 0x00, 0x00, 0x1B, 0xD7, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x48, + 0x00, 0x00, 0x11, 0x25, 0x00, 0x00, 0xB1, 0x20 + }; + + size = sizeof (kColorMatchProfileData); + data = kColorMatchProfileData; + + return true; + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_ColorMatch::Get () + { + + static dng_space_ColorMatch static_space; + + return static_space; + + } + +/*****************************************************************************/ + +dng_space_ProPhoto::dng_space_ProPhoto () + { + + SetMatrixToPCS (dng_matrix_3by3 (0.7977, 0.1352, 0.0313, + 0.2880, 0.7119, 0.0001, + 0.0000, 0.0000, 0.8249)); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_space_ProPhoto::GammaFunction () const + { + + return dng_function_GammaEncode_1_8::Get (); + + } + +/*****************************************************************************/ + +bool dng_space_ProPhoto::ICCProfile (uint32 &size, + const uint8 *&data) const + + { + + static const uint8 kProPhotoProfileData [] = + { + 0x00, 0x00, 0x03, 0xAC, 0x4B, 0x43, 0x4D, 0x53, 0x02, 0x10, 0x00, 0x00, + 0x6D, 0x6E, 0x74, 0x72, 0x52, 0x47, 0x42, 0x20, 0x58, 0x59, 0x5A, 0x20, + 0x07, 0xCE, 0x00, 0x0C, 0x00, 0x01, 0x00, 0x12, 0x00, 0x3A, 0x00, 0x15, + 0x61, 0x63, 0x73, 0x70, 0x4D, 0x53, 0x46, 0x54, 0x00, 0x00, 0x00, 0x00, + 0x4B, 0x4F, 0x44, 0x41, 0x52, 0x4F, 0x4D, 0x4D, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2B, 0x4B, 0x4F, 0x44, 0x41, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0C, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x01, 0x14, 0x00, 0x00, 0x00, 0x48, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x01, 0x5C, 0x00, 0x00, 0x00, 0x83, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x00, 0x14, + 0x72, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x0E, + 0x67, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x0E, + 0x62, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0xF4, 0x00, 0x00, 0x00, 0x0E, + 0x72, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x14, + 0x67, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x18, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x58, 0x59, 0x5A, 0x00, 0x00, 0x02, 0x2C, 0x00, 0x00, 0x00, 0x14, + 0x64, 0x6D, 0x6E, 0x64, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00, 0x00, 0x6E, + 0x64, 0x6D, 0x64, 0x64, 0x00, 0x00, 0x02, 0xB0, 0x00, 0x00, 0x00, 0xD1, + 0x6D, 0x6D, 0x6F, 0x64, 0x00, 0x00, 0x03, 0x84, 0x00, 0x00, 0x00, 0x28, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x28, 0x63, 0x29, 0x20, 0x45, 0x61, + 0x73, 0x74, 0x6D, 0x61, 0x6E, 0x20, 0x4B, 0x6F, 0x64, 0x61, 0x6B, 0x20, + 0x43, 0x6F, 0x6D, 0x70, 0x61, 0x6E, 0x79, 0x2C, 0x20, 0x31, 0x39, 0x39, + 0x39, 0x2C, 0x20, 0x61, 0x6C, 0x6C, 0x20, 0x72, 0x69, 0x67, 0x68, 0x74, + 0x73, 0x20, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x64, 0x2E, 0x00, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0D, + 0x50, 0x72, 0x6F, 0x50, 0x68, 0x6F, 0x74, 0x6F, 0x20, 0x52, 0x47, 0x42, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0E, 0xFE, 0xFF, 0x00, + 0x50, 0x00, 0x72, 0x00, 0x6F, 0x00, 0x50, 0x00, 0x68, 0x00, 0x6F, 0x00, + 0x74, 0x00, 0x6F, 0x00, 0x20, 0x00, 0x52, 0x00, 0x47, 0x00, 0x42, 0x00, + 0x00, 0x00, 0x00, 0x0D, 0x50, 0x72, 0x6F, 0x50, 0x68, 0x6F, 0x74, 0x6F, + 0x20, 0x52, 0x47, 0x42, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2C, 0x63, 0x75, 0x72, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xCD, 0x00, 0x00, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xCC, 0x34, + 0x00, 0x00, 0x49, 0xBD, 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x22, 0x9C, 0x00, 0x00, 0xB6, 0x3E, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x08, 0x06, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0xD3, 0x2D, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, + 0x4B, 0x4F, 0x44, 0x41, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x07, 0xFE, 0xFF, 0x00, 0x4B, 0x00, 0x4F, 0x00, 0x44, 0x00, 0x41, + 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x06, 0x4B, 0x4F, 0x44, 0x41, 0x4B, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x27, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6E, 0x63, + 0x65, 0x20, 0x4F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x4D, 0x65, 0x64, + 0x69, 0x75, 0x6D, 0x20, 0x4D, 0x65, 0x74, 0x72, 0x69, 0x63, 0x28, 0x52, + 0x4F, 0x4D, 0x4D, 0x29, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x28, 0xFE, 0xFF, 0x00, 0x52, 0x00, 0x65, 0x00, 0x66, 0x00, + 0x65, 0x00, 0x72, 0x00, 0x65, 0x00, 0x6E, 0x00, 0x63, 0x00, 0x65, 0x00, + 0x20, 0x00, 0x4F, 0x00, 0x75, 0x00, 0x74, 0x00, 0x70, 0x00, 0x75, 0x00, + 0x74, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x65, 0x00, 0x64, 0x00, 0x69, 0x00, + 0x75, 0x00, 0x6D, 0x00, 0x20, 0x00, 0x4D, 0x00, 0x65, 0x00, 0x74, 0x00, + 0x72, 0x00, 0x69, 0x00, 0x63, 0x00, 0x28, 0x00, 0x52, 0x00, 0x4F, 0x00, + 0x4D, 0x00, 0x4D, 0x00, 0x29, 0x00, 0x20, 0x00, 0x20, 0x00, 0x00, 0x00, + 0x00, 0x27, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6E, 0x63, 0x65, 0x20, + 0x4F, 0x75, 0x74, 0x70, 0x75, 0x74, 0x20, 0x4D, 0x65, 0x64, 0x69, 0x75, + 0x6D, 0x20, 0x4D, 0x65, 0x74, 0x72, 0x69, 0x63, 0x28, 0x52, 0x4F, 0x4D, + 0x4D, 0x29, 0x20, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x6D, 0x6D, 0x6F, 0x64, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x10, + 0x00, 0x00, 0x9D, 0x03, 0x01, 0x01, 0x01, 0x01, 0xB0, 0xCF, 0x3B, 0x80, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00 + }; + + size = sizeof (kProPhotoProfileData); + data = kProPhotoProfileData; + + return true; + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_ProPhoto::Get () + { + + static dng_space_ProPhoto static_space; + + return static_space; + + } + +/*****************************************************************************/ + +dng_space_GrayGamma18::dng_space_GrayGamma18 () + { + + SetMonochrome (); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_space_GrayGamma18::GammaFunction () const + { + + return dng_function_GammaEncode_1_8::Get (); + + } + +/*****************************************************************************/ + +bool dng_space_GrayGamma18::ICCProfile (uint32 &size, + const uint8 *&data) const + + { + + static const uint8 kGamma18ProfileData [] = + { + 0x00, 0x00, 0x01, 0x98, 0x41, 0x44, 0x42, 0x45, 0x02, 0x10, 0x00, 0x00, + 0x6D, 0x6E, 0x74, 0x72, 0x47, 0x52, 0x41, 0x59, 0x58, 0x59, 0x5A, 0x20, + 0x07, 0xCF, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x63, 0x73, 0x70, 0x41, 0x50, 0x50, 0x4C, 0x00, 0x00, 0x00, 0x00, + 0x6E, 0x6F, 0x6E, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2D, 0x41, 0x44, 0x42, 0x45, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x32, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x69, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x6B, 0x70, 0x74, 0x00, 0x00, 0x01, 0x74, 0x00, 0x00, 0x00, 0x14, + 0x6B, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x0E, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x41, + 0x64, 0x6F, 0x62, 0x65, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x73, + 0x20, 0x49, 0x6E, 0x63, 0x6F, 0x72, 0x70, 0x6F, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x47, 0x72, 0x61, 0x79, 0x20, 0x47, 0x61, 0x6D, + 0x6D, 0x61, 0x20, 0x31, 0x2E, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF3, 0x54, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xCF, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0xCD, 0x00, 0x00 + }; + + size = sizeof (kGamma18ProfileData); + data = kGamma18ProfileData; + + return true; + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_GrayGamma18::Get () + { + + static dng_space_GrayGamma18 static_space; + + return static_space; + + } + +/*****************************************************************************/ + +dng_space_GrayGamma22::dng_space_GrayGamma22 () + { + + SetMonochrome (); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_space_GrayGamma22::GammaFunction () const + { + + return dng_function_GammaEncode_2_2::Get (); + + } + +/*****************************************************************************/ + +bool dng_space_GrayGamma22::ICCProfile (uint32 &size, + const uint8 *&data) const + + { + + static const uint8 kGamma22ProfileData [] = + { + 0x00, 0x00, 0x01, 0x98, 0x41, 0x44, 0x42, 0x45, 0x02, 0x10, 0x00, 0x00, + 0x6D, 0x6E, 0x74, 0x72, 0x47, 0x52, 0x41, 0x59, 0x58, 0x59, 0x5A, 0x20, + 0x07, 0xCF, 0x00, 0x06, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x61, 0x63, 0x73, 0x70, 0x41, 0x50, 0x50, 0x4C, 0x00, 0x00, 0x00, 0x00, + 0x6E, 0x6F, 0x6E, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF6, 0xD6, + 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xD3, 0x2D, 0x41, 0x44, 0x42, 0x45, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, + 0x63, 0x70, 0x72, 0x74, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x00, 0x32, + 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0xF4, 0x00, 0x00, 0x00, 0x69, + 0x77, 0x74, 0x70, 0x74, 0x00, 0x00, 0x01, 0x60, 0x00, 0x00, 0x00, 0x14, + 0x62, 0x6B, 0x70, 0x74, 0x00, 0x00, 0x01, 0x74, 0x00, 0x00, 0x00, 0x14, + 0x6B, 0x54, 0x52, 0x43, 0x00, 0x00, 0x01, 0x88, 0x00, 0x00, 0x00, 0x0E, + 0x74, 0x65, 0x78, 0x74, 0x00, 0x00, 0x00, 0x00, 0x43, 0x6F, 0x70, 0x79, + 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x31, 0x39, 0x39, 0x39, 0x20, 0x41, + 0x64, 0x6F, 0x62, 0x65, 0x20, 0x53, 0x79, 0x73, 0x74, 0x65, 0x6D, 0x73, + 0x20, 0x49, 0x6E, 0x63, 0x6F, 0x72, 0x70, 0x6F, 0x72, 0x61, 0x74, 0x65, + 0x64, 0x00, 0x00, 0x00, 0x64, 0x65, 0x73, 0x63, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x0F, 0x47, 0x72, 0x61, 0x79, 0x20, 0x47, 0x61, 0x6D, + 0x6D, 0x61, 0x20, 0x32, 0x2E, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0xF3, 0x54, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x16, 0xCF, + 0x58, 0x59, 0x5A, 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x63, 0x75, 0x72, 0x76, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x33, 0x00, 0x00 + }; + + size = sizeof (kGamma22ProfileData); + data = kGamma22ProfileData; + + return true; + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_GrayGamma22::Get () + { + + static dng_space_GrayGamma22 static_space; + + return static_space; + + } + +/*****************************************************************************/ + +dng_space_fakeRGB::dng_space_fakeRGB () + { + + SetMatrixToPCS (dng_matrix_3by3 (0.6097, 0.2053, 0.1492, + 0.3111, 0.6257, 0.0632, + 0.0195, 0.0609, 0.7446)); + + } + +/*****************************************************************************/ + +const dng_color_space & dng_space_fakeRGB::Get () + { + + static dng_space_fakeRGB static_space; + + return static_space; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_color_space.h b/source/lib/dng_sdk/dng_color_space.h new file mode 100644 index 0000000..00d0d14 --- /dev/null +++ b/source/lib/dng_sdk/dng_color_space.h @@ -0,0 +1,351 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_color_space.h#2 $ */ +/* $DateTime: 2012/07/11 10:36:56 $ */ +/* $Change: 838485 $ */ +/* $Author: tknoll $ */ + +/** \file + * Standard gamma functions and color spaces used within the DNG SDK. + */ + +#ifndef __dng_color_space__ +#define __dng_color_space__ + +/*****************************************************************************/ + +#include "dng_1d_function.h" +#include "dng_classes.h" +#include "dng_matrix.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief A dng_1d_function for gamma encoding in sRGB color space + +class dng_function_GammaEncode_sRGB: public dng_1d_function + { + + public: + + virtual real64 Evaluate (real64 x) const; + + virtual real64 EvaluateInverse (real64 y) const; + + static const dng_1d_function & Get (); + + }; + +/*****************************************************************************/ + +/// \brief A dng_1d_function for gamma encoding with 1.8 gamma. + +class dng_function_GammaEncode_1_8: public dng_1d_function + { + + public: + + virtual real64 Evaluate (real64 x) const; + + virtual real64 EvaluateInverse (real64 y) const; + + static const dng_1d_function & Get (); + + }; + +/*****************************************************************************/ + +/// \brief A dng_1d_function for gamma encoding with 2.2 gamma. + +class dng_function_GammaEncode_2_2: public dng_1d_function + { + + public: + + virtual real64 Evaluate (real64 x) const; + + virtual real64 EvaluateInverse (real64 y) const; + + static const dng_1d_function & Get (); + + }; + +/*****************************************************************************/ + +/// \brief An abstract color space + +class dng_color_space + { + + protected: + + dng_matrix fMatrixToPCS; + + dng_matrix fMatrixFromPCS; + + public: + + virtual ~dng_color_space (); + + /// Return a matrix which transforms source data in this color space into the + /// Profile Connection Space. + + const dng_matrix & MatrixToPCS () const + { + return fMatrixToPCS; + } + + /// Return a matrix which transforms Profile Connection Space data into this + /// color space. + + const dng_matrix & MatrixFromPCS () const + { + return fMatrixFromPCS; + } + + /// Predicate which is true if this color space is monochrome (has only a + /// single column). + + bool IsMonochrome () const + { + return fMatrixToPCS.Cols () == 1; + } + + /// Getter for the gamma function for this color space. + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns true if this color space is linear. (I.e. has gamma 1.0.) + + bool IsLinear () const + { + return GammaFunction ().IsIdentity (); + } + + /// Map an input value through this color space's encoding gamma. + + real64 GammaEncode (real64 x) const + { + return GammaFunction ().Evaluate (x); + } + + /// Map an input value through this color space's decoding gamma (inverse of + /// the encoding gamma). + + real64 GammaDecode (real64 y) const + { + return GammaFunction ().EvaluateInverse (y); + } + + /// Getter for ICC profile, if this color space has one. + /// \param size Out parameter which receives size on return. + /// \param data Receives bytes of profile. + /// \retval Returns true if this color space has an ICC profile, false otherwise. + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + protected: + + dng_color_space (); + + void SetMonochrome (); + + void SetMatrixToPCS (const dng_matrix_3by3 &M); + + }; + +/*****************************************************************************/ + +/// Singleton class for sRGB color space. + +class dng_space_sRGB: public dng_color_space + { + + protected: + + dng_space_sRGB (); + + public: + + /// Returns dng_function_GammaEncode_sRGB + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns sRGB IEC61966-2.1 ICC profile + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + /// Static method for getting single global instance of this color space. + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +/// \brief Singleton class for AdobeRGB color space. + +class dng_space_AdobeRGB: public dng_color_space + { + + protected: + + dng_space_AdobeRGB (); + + public: + + /// Returns dng_function_GammaEncode_1_8 + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns AdobeRGB (1998) ICC profile + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + /// Static method for getting single global instance of this color space. + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +/// \brief Singleton class for ColorMatch color space. + +class dng_space_ColorMatch: public dng_color_space + { + + protected: + + dng_space_ColorMatch (); + + public: + + /// Returns dng_function_GammaEncode_1_8 + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns ColorMatch RGB ICC profile + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + /// Static method for getting single global instance of this color space. + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +/// \brief Singleton class for ProPhoto RGB color space. + +class dng_space_ProPhoto: public dng_color_space + { + + protected: + + dng_space_ProPhoto (); + + public: + + /// Returns dng_function_GammaEncode_1_8 + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns ProPhoto RGB ICC profile + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + /// Static method for getting single global instance of this color space. + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +/// \brief Singleton class for gamma 1.8 grayscale color space. + +class dng_space_GrayGamma18: public dng_color_space + { + + protected: + + dng_space_GrayGamma18 (); + + public: + + /// Returns dng_function_GammaEncode_1_8 + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns simple grayscale gamma 1.8 ICC profile + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + /// Static method for getting single global instance of this color space. + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +/// \brief Singleton class for gamma 2.2 grayscale color space. + +class dng_space_GrayGamma22: public dng_color_space + { + + protected: + + dng_space_GrayGamma22 (); + + public: + + /// Returns dng_function_GammaEncode_2_2 + + virtual const dng_1d_function & GammaFunction () const; + + /// Returns simple grayscale gamma 2.2 ICC profile + + virtual bool ICCProfile (uint32 &size, + const uint8 *&data) const; + + /// Static method for getting single global instance of this color space. + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +class dng_space_fakeRGB: public dng_color_space + { + + protected: + + dng_space_fakeRGB (); + + public: + + static const dng_color_space & Get (); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_color_spec.cpp b/source/lib/dng_sdk/dng_color_spec.cpp new file mode 100644 index 0000000..139cdee --- /dev/null +++ b/source/lib/dng_sdk/dng_color_spec.cpp @@ -0,0 +1,559 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_color_spec.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +#include "dng_color_spec.h" + +#include "dng_assertions.h" +#include "dng_camera_profile.h" +#include "dng_exceptions.h" +#include "dng_matrix.h" +#include "dng_negative.h" +#include "dng_temperature.h" +#include "dng_utils.h" +#include "dng_xy_coord.h" + +/*****************************************************************************/ + +dng_matrix_3by3 MapWhiteMatrix (const dng_xy_coord &white1, + const dng_xy_coord &white2) + { + + // Use the linearized Bradford adaptation matrix. + + dng_matrix_3by3 Mb ( 0.8951, 0.2664, -0.1614, + -0.7502, 1.7135, 0.0367, + 0.0389, -0.0685, 1.0296); + + dng_vector_3 w1 = Mb * XYtoXYZ (white1); + dng_vector_3 w2 = Mb * XYtoXYZ (white2); + + // Negative white coordinates are kind of meaningless. + + w1 [0] = Max_real64 (w1 [0], 0.0); + w1 [1] = Max_real64 (w1 [1], 0.0); + w1 [2] = Max_real64 (w1 [2], 0.0); + + w2 [0] = Max_real64 (w2 [0], 0.0); + w2 [1] = Max_real64 (w2 [1], 0.0); + w2 [2] = Max_real64 (w2 [2], 0.0); + + // Limit scaling to something reasonable. + + dng_matrix_3by3 A; + + A [0] [0] = Pin_real64 (0.1, w1 [0] > 0.0 ? w2 [0] / w1 [0] : 10.0, 10.0); + A [1] [1] = Pin_real64 (0.1, w1 [1] > 0.0 ? w2 [1] / w1 [1] : 10.0, 10.0); + A [2] [2] = Pin_real64 (0.1, w1 [2] > 0.0 ? w2 [2] / w1 [2] : 10.0, 10.0); + + dng_matrix_3by3 B = Invert (Mb) * A * Mb; + + return B; + + } + +/******************************************************************************/ + +dng_color_spec::dng_color_spec (const dng_negative &negative, + const dng_camera_profile *profile) + + : fChannels (negative.ColorChannels ()) + + , fTemperature1 (0.0) + , fTemperature2 (0.0) + + , fColorMatrix1 () + , fColorMatrix2 () + + , fForwardMatrix1 () + , fForwardMatrix2 () + + , fReductionMatrix1 () + , fReductionMatrix2 () + + , fCameraCalibration1 () + , fCameraCalibration2 () + + , fAnalogBalance () + + , fWhiteXY () + + , fCameraWhite () + , fCameraToPCS () + + , fPCStoCamera () + + { + + if (fChannels > 1) + { + + if (!profile || !profile->IsValid (fChannels)) + { + ThrowBadFormat (); + } + + if (profile->WasStubbed ()) + { + ThrowProgramError ("Using stubbed profile"); + } + + fTemperature1 = profile->CalibrationTemperature1 (); + fTemperature2 = profile->CalibrationTemperature2 (); + + fColorMatrix1 = profile->ColorMatrix1 (); + fColorMatrix2 = profile->ColorMatrix2 (); + + fForwardMatrix1 = profile->ForwardMatrix1 (); + fForwardMatrix2 = profile->ForwardMatrix2 (); + + fReductionMatrix1 = profile->ReductionMatrix1 (); + fReductionMatrix2 = profile->ReductionMatrix2 (); + + fCameraCalibration1.SetIdentity (fChannels); + fCameraCalibration2.SetIdentity (fChannels); + + if (negative. CameraCalibrationSignature () == + profile->ProfileCalibrationSignature ()) + { + + if (negative.CameraCalibration1 ().Rows () == fChannels && + negative.CameraCalibration1 ().Cols () == fChannels) + { + + fCameraCalibration1 = negative.CameraCalibration1 (); + + } + + if (negative.CameraCalibration2 ().Rows () == fChannels && + negative.CameraCalibration2 ().Cols () == fChannels) + { + + fCameraCalibration2 = negative.CameraCalibration2 (); + + } + + } + + fAnalogBalance = dng_matrix (fChannels, fChannels); + + for (uint32 j = 0; j < fChannels; j++) + { + + fAnalogBalance [j] [j] = negative.AnalogBalance (j); + + } + + dng_camera_profile::NormalizeForwardMatrix (fForwardMatrix1); + + fColorMatrix1 = fAnalogBalance * fCameraCalibration1 * fColorMatrix1; + + if (!profile->HasColorMatrix2 () || + fTemperature1 <= 0.0 || + fTemperature2 <= 0.0 || + fTemperature1 == fTemperature2) + { + + fTemperature1 = 5000.0; + fTemperature2 = 5000.0; + + fColorMatrix2 = fColorMatrix1; + fForwardMatrix2 = fForwardMatrix1; + fReductionMatrix2 = fReductionMatrix1; + fCameraCalibration2 = fCameraCalibration1; + + } + + else + { + + dng_camera_profile::NormalizeForwardMatrix (fForwardMatrix2); + + fColorMatrix2 = fAnalogBalance * fCameraCalibration2 * fColorMatrix2; + + // Swap values if temperatures are out of order. + + if (fTemperature1 > fTemperature2) + { + + real64 temp = fTemperature1; + fTemperature1 = fTemperature2; + fTemperature2 = temp; + + dng_matrix T = fColorMatrix1; + fColorMatrix1 = fColorMatrix2; + fColorMatrix2 = T; + + T = fForwardMatrix1; + fForwardMatrix1 = fForwardMatrix2; + fForwardMatrix2 = T; + + T = fReductionMatrix1; + fReductionMatrix1 = fReductionMatrix2; + fReductionMatrix2 = T; + + T = fCameraCalibration1; + fCameraCalibration1 = fCameraCalibration2; + fCameraCalibration2 = T; + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_matrix dng_color_spec::FindXYZtoCamera (const dng_xy_coord &white, + dng_matrix *forwardMatrix, + dng_matrix *reductionMatrix, + dng_matrix *cameraCalibration) + { + + // Convert to temperature/offset space. + + dng_temperature td (white); + + // Find fraction to weight the first calibration. + + real64 g; + + if (td.Temperature () <= fTemperature1) + g = 1.0; + + else if (td.Temperature () >= fTemperature2) + g = 0.0; + + else + { + + real64 invT = 1.0 / td.Temperature (); + + g = (invT - (1.0 / fTemperature2)) / + ((1.0 / fTemperature1) - (1.0 / fTemperature2)); + + } + + // Interpolate the color matrix. + + dng_matrix colorMatrix; + + if (g >= 1.0) + colorMatrix = fColorMatrix1; + + else if (g <= 0.0) + colorMatrix = fColorMatrix2; + + else + colorMatrix = (g ) * fColorMatrix1 + + (1.0 - g) * fColorMatrix2; + + // Interpolate forward matrix, if any. + + if (forwardMatrix) + { + + bool has1 = fForwardMatrix1.NotEmpty (); + bool has2 = fForwardMatrix2.NotEmpty (); + + if (has1 && has2) + { + + if (g >= 1.0) + *forwardMatrix = fForwardMatrix1; + + else if (g <= 0.0) + *forwardMatrix = fForwardMatrix2; + + else + *forwardMatrix = (g ) * fForwardMatrix1 + + (1.0 - g) * fForwardMatrix2; + + } + + else if (has1) + { + + *forwardMatrix = fForwardMatrix1; + + } + + else if (has2) + { + + *forwardMatrix = fForwardMatrix2; + + } + + else + { + + forwardMatrix->Clear (); + + } + + } + + // Interpolate reduction matrix, if any. + + if (reductionMatrix) + { + + bool has1 = fReductionMatrix1.NotEmpty (); + bool has2 = fReductionMatrix2.NotEmpty (); + + if (has1 && has2) + { + + if (g >= 1.0) + *reductionMatrix = fReductionMatrix1; + + else if (g <= 0.0) + *reductionMatrix = fReductionMatrix2; + + else + *reductionMatrix = (g ) * fReductionMatrix1 + + (1.0 - g) * fReductionMatrix2; + + } + + else if (has1) + { + + *reductionMatrix = fReductionMatrix1; + + } + + else if (has2) + { + + *reductionMatrix = fReductionMatrix2; + + } + + else + { + + reductionMatrix->Clear (); + + } + + } + + // Interpolate camera calibration matrix. + + if (cameraCalibration) + { + + if (g >= 1.0) + *cameraCalibration = fCameraCalibration1; + + else if (g <= 0.0) + *cameraCalibration = fCameraCalibration2; + + else + *cameraCalibration = (g ) * fCameraCalibration1 + + (1.0 - g) * fCameraCalibration2; + + } + + // Return the interpolated color matrix. + + return colorMatrix; + + } + +/*****************************************************************************/ + +void dng_color_spec::SetWhiteXY (const dng_xy_coord &white) + { + + fWhiteXY = white; + + // Deal with monochrome cameras. + + if (fChannels == 1) + { + + fCameraWhite.SetIdentity (1); + + fCameraToPCS = PCStoXYZ ().AsColumn (); + + return; + + } + + // Interpolate an matric values for this white point. + + dng_matrix colorMatrix; + dng_matrix forwardMatrix; + dng_matrix reductionMatrix; + dng_matrix cameraCalibration; + + colorMatrix = FindXYZtoCamera (fWhiteXY, + &forwardMatrix, + &reductionMatrix, + &cameraCalibration); + + // Find the camera white values. + + fCameraWhite = colorMatrix * XYtoXYZ (fWhiteXY); + + real64 whiteScale = 1.0 / MaxEntry (fCameraWhite); + + for (uint32 j = 0; j < fChannels; j++) + { + + // We don't support non-positive values for camera neutral values. + + fCameraWhite [j] = Pin_real64 (0.001, + whiteScale * fCameraWhite [j], + 1.0); + + } + + // Find PCS to Camera transform. Scale matrix so PCS white can just be + // reached when the first camera channel saturates + + fPCStoCamera = colorMatrix * MapWhiteMatrix (PCStoXY (), fWhiteXY); + + real64 scale = MaxEntry (fPCStoCamera * PCStoXYZ ()); + + fPCStoCamera = (1.0 / scale) * fPCStoCamera; + + // If we have a forward matrix, then just use that. + + if (forwardMatrix.NotEmpty ()) + { + + dng_matrix individualToReference = Invert (fAnalogBalance * cameraCalibration); + + dng_vector refCameraWhite = individualToReference * fCameraWhite; + + fCameraToPCS = forwardMatrix * + Invert (refCameraWhite.AsDiagonal ()) * + individualToReference; + + } + + // Else we need to use the adapt in XYZ method. + + else + { + + // Invert this PCS to camera matrix. Note that if there are more than three + // camera channels, this inversion is non-unique. + + fCameraToPCS = Invert (fPCStoCamera, reductionMatrix); + + } + + } + +/*****************************************************************************/ + +const dng_xy_coord & dng_color_spec::WhiteXY () const + { + + DNG_ASSERT (fWhiteXY.IsValid (), "Using invalid WhiteXY"); + + return fWhiteXY; + + } + +/*****************************************************************************/ + +const dng_vector & dng_color_spec::CameraWhite () const + { + + DNG_ASSERT (fCameraWhite.NotEmpty (), "Using invalid CameraWhite"); + + return fCameraWhite; + + } + +/*****************************************************************************/ + +const dng_matrix & dng_color_spec::CameraToPCS () const + { + + DNG_ASSERT (fCameraToPCS.NotEmpty (), "Using invalid CameraToPCS"); + + return fCameraToPCS; + + } + +/*****************************************************************************/ + +const dng_matrix & dng_color_spec::PCStoCamera () const + { + + DNG_ASSERT (fPCStoCamera.NotEmpty (), "Using invalid PCStoCamera"); + + return fPCStoCamera; + + } + +/*****************************************************************************/ + +dng_xy_coord dng_color_spec::NeutralToXY (const dng_vector &neutral) + { + + const uint32 kMaxPasses = 30; + + if (fChannels == 1) + { + + return PCStoXY (); + + } + + dng_xy_coord last = D50_xy_coord (); + + for (uint32 pass = 0; pass < kMaxPasses; pass++) + { + + dng_matrix xyzToCamera = FindXYZtoCamera (last); + + dng_xy_coord next = XYZtoXY (Invert (xyzToCamera) * neutral); + + if (Abs_real64 (next.x - last.x) + + Abs_real64 (next.y - last.y) < 0.0000001) + { + + return next; + + } + + // If we reach the limit without converging, we are most likely + // in a two value oscillation. So take the average of the last + // two estimates and give up. + + if (pass == kMaxPasses - 1) + { + + next.x = (last.x + next.x) * 0.5; + next.y = (last.y + next.y) * 0.5; + + } + + last = next; + + } + + return last; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_color_spec.h b/source/lib/dng_sdk/dng_color_spec.h new file mode 100644 index 0000000..0d06dde --- /dev/null +++ b/source/lib/dng_sdk/dng_color_spec.h @@ -0,0 +1,146 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_color_spec.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Class for holding a specific color transform. +*/ + +#ifndef __dng_color_spec__ +#define __dng_color_spec__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_matrix.h" +#include "dng_types.h" +#include "dng_xy_coord.h" + +/*****************************************************************************/ + +/// \brief Compute a 3x3 matrix which maps colors from white point white1 to +/// white point white2 +/// +/// Uses linearized Bradford adaptation matrix to compute a mapping from +/// colors measured with one white point (white1) to another (white2). + +dng_matrix_3by3 MapWhiteMatrix (const dng_xy_coord &white1, + const dng_xy_coord &white2); + +/*****************************************************************************/ + +/// Color transform taking into account white point and camera calibration and +/// individual calibration from DNG negative. + +class dng_color_spec + { + + private: + + uint32 fChannels; + + real64 fTemperature1; + real64 fTemperature2; + + dng_matrix fColorMatrix1; + dng_matrix fColorMatrix2; + + dng_matrix fForwardMatrix1; + dng_matrix fForwardMatrix2; + + dng_matrix fReductionMatrix1; + dng_matrix fReductionMatrix2; + + dng_matrix fCameraCalibration1; + dng_matrix fCameraCalibration2; + + dng_matrix fAnalogBalance; + + dng_xy_coord fWhiteXY; + + dng_vector fCameraWhite; + dng_matrix fCameraToPCS; + + dng_matrix fPCStoCamera; + + public: + + /// Read calibration info from DNG negative and construct a + /// dng_color_spec. + + dng_color_spec (const dng_negative &negative, + const dng_camera_profile *profile); + + virtual ~dng_color_spec () + { + } + + /// Number of channels used for this color transform. Three + /// for most cameras. + + uint32 Channels () const + { + return fChannels; + } + + /// Setter for white point. Value is as XY colorspace coordinate. + /// \param white White point to set as an XY value. + + void SetWhiteXY (const dng_xy_coord &white); + + /// Getter for white point. Value is as XY colorspace coordinate. + /// \retval XY value of white point. + + const dng_xy_coord & WhiteXY () const; + + /// Return white point in camera native color coordinates. + /// \retval A dng_vector with components ranging from 0.0 to 1.0 + /// that is normalized such that one component is equal to 1.0 . + + const dng_vector & CameraWhite () const; + + /// Getter for camera to Profile Connection Space color transform. + /// \retval A transform that takes into account all camera calibration + /// transforms and white point. + + const dng_matrix & CameraToPCS () const; + + /// Getter for Profile Connection Space to camera color transform. + /// \retval A transform that takes into account all camera calibration + /// transforms and white point. + + const dng_matrix & PCStoCamera () const; + + /// Return the XY value to use for SetWhiteXY for a given camera color + /// space coordinate as the white point. + /// \param neutral A camera color space value to use for white point. + /// Components range from 0.0 to 1.0 and should be normalized such that + /// the largest value is 1.0 . + /// \retval White point in XY space that makes neutral map to this + /// XY value as closely as possible. + + dng_xy_coord NeutralToXY (const dng_vector &neutral); + + private: + + dng_matrix FindXYZtoCamera (const dng_xy_coord &white, + dng_matrix *forwardMatrix = NULL, + dng_matrix *reductionMatrix = NULL, + dng_matrix *cameraCalibration = NULL); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_date_time.cpp b/source/lib/dng_sdk/dng_date_time.cpp new file mode 100644 index 0000000..770d300 --- /dev/null +++ b/source/lib/dng_sdk/dng_date_time.cpp @@ -0,0 +1,961 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_date_time.cpp#2 $ */ +/* $DateTime: 2012/06/01 07:28:57 $ */ +/* $Change: 832715 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_date_time.h" + +#include "dng_exceptions.h" +#include "dng_mutex.h" +#include "dng_stream.h" +#include "dng_string.h" +#include "dng_utils.h" + +#include + +#include "stdc_includes.h" + +#if qMacOS && qEnableCarbon +#include +#endif + +#if qWinOS +#include +#endif + +/******************************************************************************/ + +// MWG says don't use fake time zones in XMP, but there is some +// old software that requires them to work correctly. + +bool gDNGUseFakeTimeZonesInXMP = false; + +/******************************************************************************/ + +dng_date_time::dng_date_time () + + : fYear (0) + , fMonth (0) + , fDay (0) + , fHour (0) + , fMinute (0) + , fSecond (0) + + { + + } + +/******************************************************************************/ + +dng_date_time::dng_date_time (uint32 year, + uint32 month, + uint32 day, + uint32 hour, + uint32 minute, + uint32 second) + + : fYear (year) + , fMonth (month) + , fDay (day) + , fHour (hour) + , fMinute (minute) + , fSecond (second) + + { + + } + +/******************************************************************************/ + +bool dng_date_time::IsValid () const + { + + return fYear >= 1 && fYear <= 9999 && + fMonth >= 1 && fMonth <= 12 && + fDay >= 1 && fDay <= 31 && + fHour <= 23 && + fMinute <= 59 && + fSecond <= 59; + + } + +/*****************************************************************************/ + +void dng_date_time::Clear () + { + + *this = dng_date_time (); + + } + +/*****************************************************************************/ + +static uint32 DateTimeParseU32 (const char *&s) + { + + uint32 x = 0; + + while (*s == ' ' || *s == ':') + s++; + + while (*s >= '0' && *s <= '9') + { + x = x * 10 + (uint32) (*(s++) - '0'); + } + + return x; + + } + +/*****************************************************************************/ + +bool dng_date_time::Parse (const char *s) + { + + fYear = DateTimeParseU32 (s); + fMonth = DateTimeParseU32 (s); + fDay = DateTimeParseU32 (s); + fHour = DateTimeParseU32 (s); + fMinute = DateTimeParseU32 (s); + fSecond = DateTimeParseU32 (s); + + return IsValid (); + + } + +/*****************************************************************************/ + +dng_string dng_time_zone::Encode_ISO_8601 () const + { + + dng_string result; + + if (IsValid ()) + { + + if (OffsetMinutes () == 0) + { + + result.Set ("Z"); + + } + + else + { + + char s [64]; + + int offset = OffsetMinutes (); + + if (offset > 0) + { + + sprintf (s, "+%02d:%02d", offset / 60, offset % 60); + + } + + else + { + + offset = -offset; + + sprintf (s, "-%02d:%02d", offset / 60, offset % 60); + + } + + result.Set (s); + + } + + } + + return result; + + } + +/*****************************************************************************/ + +dng_date_time_info::dng_date_time_info () + + : fDateOnly (true) + , fDateTime () + , fSubseconds () + , fTimeZone () + + { + + } + +/*****************************************************************************/ + +bool dng_date_time_info::IsValid () const + { + + return fDateTime.IsValid (); + + } + +/*****************************************************************************/ + +void dng_date_time_info::SetDate (uint32 year, + uint32 month, + uint32 day) + { + + fDateTime.fYear = year; + fDateTime.fMonth = month; + fDateTime.fDay = day; + + } + +/*****************************************************************************/ + +void dng_date_time_info::SetTime (uint32 hour, + uint32 minute, + uint32 second) + { + + fDateOnly = false; + + fDateTime.fHour = hour; + fDateTime.fMinute = minute; + fDateTime.fSecond = second; + + } + +/*****************************************************************************/ + +void dng_date_time_info::Decode_ISO_8601 (const char *s) + { + + Clear (); + + uint32 len = (uint32) strlen (s); + + if (!len) + { + return; + } + + unsigned year = 0; + unsigned month = 0; + unsigned day = 0; + + if (sscanf (s, + "%u-%u-%u", + &year, + &month, + &day) != 3) + { + return; + } + + SetDate ((uint32) year, + (uint32) month, + (uint32) day); + + if (fDateTime.NotValid ()) + { + Clear (); + return; + } + + for (uint32 j = 0; j < len; j++) + { + + if (s [j] == 'T') + { + + unsigned hour = 0; + unsigned minute = 0; + unsigned second = 0; + + int items = sscanf (s + j + 1, + "%u:%u:%u", + &hour, + &minute, + &second); + + if (items >= 2 && items <= 3) + { + + SetTime ((uint32) hour, + (uint32) minute, + (uint32) second); + + if (fDateTime.NotValid ()) + { + Clear (); + return; + } + + if (items == 3) + { + + for (uint32 k = j + 1; k < len; k++) + { + + if (s [k] == '.') + { + + while (++k < len && s [k] >= '0' && s [k] <= '9') + { + + char ss [2]; + + ss [0] = s [k]; + ss [1] = 0; + + fSubseconds.Append (ss); + + } + + break; + + } + + } + + } + + for (uint32 k = j + 1; k < len; k++) + { + + if (s [k] == 'Z') + { + + fTimeZone.SetOffsetMinutes (0); + + break; + + } + + if (s [k] == '+' || s [k] == '-') + { + + int32 sign = (s [k] == '-' ? -1 : 1); + + unsigned tzhour = 0; + unsigned tzmin = 0; + + if (sscanf (s + k + 1, + "%u:%u", + &tzhour, + &tzmin) > 0) + { + + fTimeZone.SetOffsetMinutes (sign * (tzhour * 60 + tzmin)); + + } + + break; + + } + + } + + } + + break; + + } + + } + + } + +/*****************************************************************************/ + +dng_string dng_date_time_info::Encode_ISO_8601 () const + { + + dng_string result; + + if (IsValid ()) + { + + char s [256]; + + sprintf (s, + "%04u-%02u-%02u", + (unsigned) fDateTime.fYear, + (unsigned) fDateTime.fMonth, + (unsigned) fDateTime.fDay); + + result.Set (s); + + if (!fDateOnly) + { + + sprintf (s, + "T%02u:%02u:%02u", + (unsigned) fDateTime.fHour, + (unsigned) fDateTime.fMinute, + (unsigned) fDateTime.fSecond); + + result.Append (s); + + if (fSubseconds.NotEmpty ()) + { + + bool subsecondsValid = true; + + uint32 len = fSubseconds.Length (); + + for (uint32 index = 0; index < len; index++) + { + + if (fSubseconds.Get () [index] < '0' || + fSubseconds.Get () [index] > '9') + { + subsecondsValid = false; + break; + } + + } + + if (subsecondsValid) + { + result.Append ("."); + result.Append (fSubseconds.Get ()); + } + + } + + if (gDNGUseFakeTimeZonesInXMP) + { + + // Kludge: Early versions of the XMP toolkit assume Zulu time + // if the time zone is missing. It is safer for fill in the + // local time zone. + + dng_time_zone tempZone = fTimeZone; + + if (tempZone.NotValid ()) + { + tempZone = LocalTimeZone (fDateTime); + } + + result.Append (tempZone.Encode_ISO_8601 ().Get ()); + + } + + else + { + + // MWG: Now we don't fill in the local time zone. So only + // add the time zone if it is known and valid. + + if (fTimeZone.IsValid ()) + { + result.Append (fTimeZone.Encode_ISO_8601 ().Get ()); + } + + } + + } + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_date_time_info::Decode_IPTC_Date (const char *s) + { + + if (strlen (s) == 8) + { + + unsigned year = 0; + unsigned month = 0; + unsigned day = 0; + + if (sscanf (s, + "%4u%2u%2u", + &year, + &month, + &day) == 3) + { + + SetDate ((uint32) year, + (uint32) month, + (uint32) day); + + } + + } + + } + +/*****************************************************************************/ + +dng_string dng_date_time_info::Encode_IPTC_Date () const + { + + dng_string result; + + if (IsValid ()) + { + + char s [64]; + + sprintf (s, + "%04u%02u%02u", + (unsigned) fDateTime.fYear, + (unsigned) fDateTime.fMonth, + (unsigned) fDateTime.fDay); + + result.Set (s); + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_date_time_info::Decode_IPTC_Time (const char *s) + { + + if (strlen (s) == 11) + { + + char time [12]; + + memcpy (time, s, sizeof (time)); + + if (time [6] == '+' || + time [6] == '-') + { + + int tzsign = (time [6] == '-') ? -1 : 1; + + time [6] = 0; + + unsigned hour = 0; + unsigned minute = 0; + unsigned second = 0; + unsigned tzhour = 0; + unsigned tzmin = 0; + + if (sscanf (time, + "%2u%2u%2u", + &hour, + &minute, + &second) == 3 && + sscanf (time + 7, + "%2u%2u", + &tzhour, + &tzmin) == 2) + { + + dng_time_zone zone; + + zone.SetOffsetMinutes (tzsign * (tzhour * 60 + tzmin)); + + if (zone.IsValid ()) + { + + SetTime ((uint32) hour, + (uint32) minute, + (uint32) second); + + SetZone (zone); + + } + + } + + } + + } + + else if (strlen (s) == 6) + { + + unsigned hour = 0; + unsigned minute = 0; + unsigned second = 0; + + if (sscanf (s, + "%2u%2u%2u", + &hour, + &minute, + &second) == 3) + { + + SetTime ((uint32) hour, + (uint32) minute, + (uint32) second); + + } + + } + + else if (strlen (s) == 4) + { + + unsigned hour = 0; + unsigned minute = 0; + + if (sscanf (s, + "%2u%2u", + &hour, + &minute) == 2) + { + + SetTime ((uint32) hour, + (uint32) minute, + 0); + + } + + } + + } + +/*****************************************************************************/ + +dng_string dng_date_time_info::Encode_IPTC_Time () const + { + + dng_string result; + + if (IsValid () && !fDateOnly) + { + + char s [64]; + + if (fTimeZone.IsValid ()) + { + + sprintf (s, + "%02u%02u%02u%c%02u%02u", + (unsigned) fDateTime.fHour, + (unsigned) fDateTime.fMinute, + (unsigned) fDateTime.fSecond, + (int) (fTimeZone.OffsetMinutes () >= 0 ? '+' : '-'), + (unsigned) (Abs_int32 (fTimeZone.OffsetMinutes ()) / 60), + (unsigned) (Abs_int32 (fTimeZone.OffsetMinutes ()) % 60)); + + } + + else + { + + sprintf (s, + "%02u%02u%02u", + (unsigned) fDateTime.fHour, + (unsigned) fDateTime.fMinute, + (unsigned) fDateTime.fSecond); + + } + + result.Set (s); + + } + + return result; + + } + +/*****************************************************************************/ + +static dng_mutex gDateTimeMutex ("gDateTimeMutex"); + +/*****************************************************************************/ + +void CurrentDateTimeAndZone (dng_date_time_info &info) + { + + time_t sec; + + time (&sec); + + tm t; + tm zt; + + { + + dng_lock_mutex lock (&gDateTimeMutex); + + t = *localtime (&sec); + zt = *gmtime (&sec); + + } + + dng_date_time dt; + + dt.fYear = t.tm_year + 1900; + dt.fMonth = t.tm_mon + 1; + dt.fDay = t.tm_mday; + dt.fHour = t.tm_hour; + dt.fMinute = t.tm_min; + dt.fSecond = t.tm_sec; + + info.SetDateTime (dt); + + int tzHour = t.tm_hour - zt.tm_hour; + int tzMin = t.tm_min - zt.tm_min; + + bool zonePositive = (t.tm_year > zt.tm_year) || + (t.tm_year == zt.tm_year && t.tm_yday > zt.tm_yday) || + (t.tm_year == zt.tm_year && t.tm_yday == zt.tm_yday && tzHour > 0) || + (t.tm_year == zt.tm_year && t.tm_yday == zt.tm_yday && tzHour == 0 && tzMin >= 0); + + tzMin += tzHour * 60; + + if (zonePositive) + { + + while (tzMin < 0) + tzMin += 24 * 60; + + } + + else + { + + while (tzMin > 0) + tzMin -= 24 * 60; + + } + + dng_time_zone zone; + + zone.SetOffsetMinutes (tzMin); + + info.SetZone (zone); + + } + +/*****************************************************************************/ + +void DecodeUnixTime (uint32 unixTime, dng_date_time &dt) + { + + time_t sec = (time_t) unixTime; + + tm t; + + { + + dng_lock_mutex lock (&gDateTimeMutex); + + #if qMacOS && !defined(__MACH__) + + // Macintosh CFM stores time in local time zone. + + tm *tp = localtime (&sec); + + #else + + // Macintosh Mach-O and Windows stores time in Zulu time. + + tm *tp = gmtime (&sec); + + #endif + + if (!tp) + { + dt.Clear (); + return; + } + + t = *tp; + + } + + dt.fYear = t.tm_year + 1900; + dt.fMonth = t.tm_mon + 1; + dt.fDay = t.tm_mday; + dt.fHour = t.tm_hour; + dt.fMinute = t.tm_min; + dt.fSecond = t.tm_sec; + + } + +/*****************************************************************************/ + +dng_time_zone LocalTimeZone (const dng_date_time &dt) + { + + dng_time_zone result; + + if (dt.IsValid ()) + { + + #if qMacOS && qEnableCarbon + + CFTimeZoneRef zoneRef = CFTimeZoneCopyDefault (); + + if (zoneRef) + { + + CFGregorianDate gregDate; + + gregDate.year = dt.fYear; + gregDate.month = (SInt8) dt.fMonth; + gregDate.day = (SInt8) dt.fDay; + gregDate.hour = (SInt8) dt.fHour; + gregDate.minute = (SInt8) dt.fMinute; + gregDate.second = (SInt8) dt.fSecond; + + CFAbsoluteTime absTime = CFGregorianDateGetAbsoluteTime (gregDate, zoneRef); + + CFTimeInterval secondsDelta = CFTimeZoneGetSecondsFromGMT (zoneRef, absTime); + + CFRelease (zoneRef); + + result.SetOffsetSeconds (Round_int32 (secondsDelta)); + + if (result.IsValid ()) + { + return result; + } + + } + + #endif + + #if qWinOS + + if (GetTimeZoneInformation != NULL && + SystemTimeToTzSpecificLocalTime != NULL && + SystemTimeToFileTime != NULL) + { + + TIME_ZONE_INFORMATION tzInfo; + + DWORD x = GetTimeZoneInformation (&tzInfo); + + SYSTEMTIME localST; + + memset (&localST, 0, sizeof (localST)); + + localST.wYear = (WORD) dt.fYear; + localST.wMonth = (WORD) dt.fMonth; + localST.wDay = (WORD) dt.fDay; + localST.wHour = (WORD) dt.fHour; + localST.wMinute = (WORD) dt.fMinute; + localST.wSecond = (WORD) dt.fSecond; + + SYSTEMTIME utcST; + + if (TzSpecificLocalTimeToSystemTime (&tzInfo, &localST, &utcST)) + { + + FILETIME localFT; + FILETIME utcFT; + + (void) SystemTimeToFileTime (&localST, &localFT); + (void) SystemTimeToFileTime (&utcST , &utcFT ); + + uint64 time1 = (((uint64) localFT.dwHighDateTime) << 32) + localFT.dwLowDateTime; + uint64 time2 = (((uint64) utcFT .dwHighDateTime) << 32) + utcFT .dwLowDateTime; + + // FILETIMEs are in units to 100 ns. Convert to seconds. + + int64 time1Sec = time1 / 10000000; + int64 time2Sec = time2 / 10000000; + + int32 delta = (int32) (time1Sec - time2Sec); + + result.SetOffsetSeconds (delta); + + if (result.IsValid ()) + { + return result; + } + + } + + } + + #endif + + } + + // Figure out local time zone. + + dng_date_time_info current_info; + + CurrentDateTimeAndZone (current_info); + + result = current_info.TimeZone (); + + return result; + + } + +/*****************************************************************************/ + +dng_date_time_storage_info::dng_date_time_storage_info () + + : fOffset (kDNGStreamInvalidOffset ) + , fFormat (dng_date_time_format_unknown) + + { + + } + +/*****************************************************************************/ + +dng_date_time_storage_info::dng_date_time_storage_info (uint64 offset, + dng_date_time_format format) + + : fOffset (offset) + , fFormat (format) + + { + + } + +/*****************************************************************************/ + +bool dng_date_time_storage_info::IsValid () const + { + + return fOffset != kDNGStreamInvalidOffset; + + } + +/*****************************************************************************/ + +uint64 dng_date_time_storage_info::Offset () const + { + + if (!IsValid ()) + ThrowProgramError (); + + return fOffset; + + } + +/*****************************************************************************/ + +dng_date_time_format dng_date_time_storage_info::Format () const + { + + if (!IsValid ()) + ThrowProgramError (); + + return fFormat; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_date_time.h b/source/lib/dng_sdk/dng_date_time.h new file mode 100644 index 0000000..3f923f2 --- /dev/null +++ b/source/lib/dng_sdk/dng_date_time.h @@ -0,0 +1,385 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_date_time.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Functions and classes for working with dates and times in DNG files. + */ + +/*****************************************************************************/ + +#ifndef __dng_date_time__ +#define __dng_date_time__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_string.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief Class for holding a date/time and converting to and from relevant +/// date/time formats + +class dng_date_time + { + + public: + + uint32 fYear; + uint32 fMonth; + uint32 fDay; + uint32 fHour; + uint32 fMinute; + uint32 fSecond; + + public: + + /// Construct an invalid date/time + + dng_date_time (); + + /// Construct a date/time with specific values. + /// \param year Year to use as actual integer value, such as 2006. + /// \param month Month to use from 1 - 12, where 1 is January. + /// \param day Day of month to use from 1 -31, where 1 is the first. + /// \param hour Hour of day to use from 0 - 23, where 0 is midnight. + /// \param minute Minute of hour to use from 0 - 59. + /// \param second Second of minute to use from 0 - 59. + + dng_date_time (uint32 year, + uint32 month, + uint32 day, + uint32 hour, + uint32 minute, + uint32 second); + + /// Predicate to determine if a date is valid. + /// \retval true if all fields are within range. + + bool IsValid () const; + + /// Predicate to determine if a date is invalid. + /// \retval true if any field is out of range. + + bool NotValid () const + { + return !IsValid (); + } + + /// Equal operator. + + bool operator== (const dng_date_time &dt) const + { + return fYear == dt.fYear && + fMonth == dt.fMonth && + fDay == dt.fDay && + fHour == dt.fHour && + fMinute == dt.fMinute && + fSecond == dt.fSecond; + } + + // Not-equal operator. + + bool operator!= (const dng_date_time &dt) const + { + return !(*this == dt); + } + + /// Set date to an invalid value. + + void Clear (); + + /// Parse an EXIF format date string. + /// \param s Input date string to parse. + /// \retval true if date was parsed successfully and date is valid. + + bool Parse (const char *s); + + }; + +/*****************************************************************************/ + +/// \brief Class for holding a time zone. + +class dng_time_zone + { + + private: + + enum + { + + kMaxOffsetHours = 15, + kMinOffsetHours = -kMaxOffsetHours, + + kMaxOffsetMinutes = kMaxOffsetHours * 60, + kMinOffsetMinutes = kMinOffsetHours * 60, + + kInvalidOffset = kMinOffsetMinutes - 1 + + }; + + // Offset from GMT in minutes. Positive numbers are + // ahead of GMT, negative number are behind GMT. + + int32 fOffsetMinutes; + + public: + + dng_time_zone () + : fOffsetMinutes (kInvalidOffset) + { + } + + void Clear () + { + fOffsetMinutes = kInvalidOffset; + } + + void SetOffsetHours (int32 offset) + { + fOffsetMinutes = offset * 60; + } + + void SetOffsetMinutes (int32 offset) + { + fOffsetMinutes = offset; + } + + void SetOffsetSeconds (int32 offset) + { + fOffsetMinutes = (offset > 0) ? ((offset + 30) / 60) + : ((offset - 30) / 60); + } + + bool IsValid () const + { + return fOffsetMinutes >= kMinOffsetMinutes && + fOffsetMinutes <= kMaxOffsetMinutes; + } + + bool NotValid () const + { + return !IsValid (); + } + + int32 OffsetMinutes () const + { + return fOffsetMinutes; + } + + bool IsExactHourOffset () const + { + return IsValid () && ((fOffsetMinutes % 60) == 0); + } + + int32 ExactHourOffset () const + { + return fOffsetMinutes / 60; + } + + dng_string Encode_ISO_8601 () const; + + }; + +/*****************************************************************************/ + +/// \brief Class for holding complete data/time/zone information. + +class dng_date_time_info + { + + private: + + // Is only the date valid and not the time? + + bool fDateOnly; + + // Date and time. + + dng_date_time fDateTime; + + // Subseconds string (stored in a separate tag in EXIF). + + dng_string fSubseconds; + + // Time zone, if known. + + dng_time_zone fTimeZone; + + public: + + dng_date_time_info (); + + bool IsValid () const; + + bool NotValid () const + { + return !IsValid (); + } + + void Clear () + { + *this = dng_date_time_info (); + } + + const dng_date_time & DateTime () const + { + return fDateTime; + } + + void SetDateTime (const dng_date_time &dt) + { + fDateOnly = false; + fDateTime = dt; + } + + const dng_string & Subseconds () const + { + return fSubseconds; + } + + void SetSubseconds (const dng_string &s) + { + fSubseconds = s; + } + + const dng_time_zone & TimeZone () const + { + return fTimeZone; + } + + void SetZone (const dng_time_zone &zone) + { + fTimeZone = zone; + } + + void Decode_ISO_8601 (const char *s); + + dng_string Encode_ISO_8601 () const; + + void Decode_IPTC_Date (const char *s); + + dng_string Encode_IPTC_Date () const; + + void Decode_IPTC_Time (const char *s); + + dng_string Encode_IPTC_Time () const; + + private: + + void SetDate (uint32 year, + uint32 month, + uint32 day); + + void SetTime (uint32 hour, + uint32 minute, + uint32 second); + + }; + +/*****************************************************************************/ + +/// Get the current date/time and timezone. +/// \param info Receives current data/time/zone. + +void CurrentDateTimeAndZone (dng_date_time_info &info); + +/*****************************************************************************/ + +/// Convert UNIX "seconds since Jan 1, 1970" time to a dng_date_time + +void DecodeUnixTime (uint32 unixTime, dng_date_time &dt); + +/*****************************************************************************/ + +/// Return timezone of current location at a given date. +/// \param dt Date at which to compute timezone difference. (For example, used +/// to determine Daylight Savings, etc.) +/// \retval Time zone for date/time dt. + +dng_time_zone LocalTimeZone (const dng_date_time &dt); + +/*****************************************************************************/ + +/// Tag to encode date represenation format + +enum dng_date_time_format + { + dng_date_time_format_unknown = 0, /// Date format not known + dng_date_time_format_exif = 1, /// EXIF date string + dng_date_time_format_unix_little_endian = 2, /// 32-bit UNIX time as 4-byte little endian + dng_date_time_format_unix_big_endian = 3 /// 32-bit UNIX time as 4-byte big endian + }; + +/*****************************************************************************/ + +/// \brief Store file offset from which date was read. +/// +/// Used internally by Adobe to update date in original file. +/// \warning Use at your own risk. + +class dng_date_time_storage_info + { + + private: + + uint64 fOffset; + + dng_date_time_format fFormat; + + public: + + /// The default constructor initializes to an invalid state. + + dng_date_time_storage_info (); + + /// Construct with file offset and date format. + + dng_date_time_storage_info (uint64 offset, + dng_date_time_format format); + + /// Predicate to determine if an offset is valid. + /// \retval true if offset is valid. + + bool IsValid () const; + + // The accessors throw if the data is not valid. + + /// Getter for offset in file. + /// \exception dng_exception with fErrorCode equal to dng_error_unknown + /// if offset is not valid. + + uint64 Offset () const; + + /// Get for format date was originally stored in file. Throws a + /// dng_error_unknown exception if offset is invalid. + /// \exception dng_exception with fErrorCode equal to dng_error_unknown + /// if offset is not valid. + + dng_date_time_format Format () const; + + }; + +/*****************************************************************************/ + +// Kludge: Global boolean to turn on fake time zones in XMP for old software. + +extern bool gDNGUseFakeTimeZonesInXMP; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_errors.h b/source/lib/dng_sdk/dng_errors.h new file mode 100644 index 0000000..44feb5a --- /dev/null +++ b/source/lib/dng_sdk/dng_errors.h @@ -0,0 +1,58 @@ +/*****************************************************************************/ +// Copyright 2006-2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_errors.h#2 $ */ +/* $DateTime: 2012/07/11 10:36:56 $ */ +/* $Change: 838485 $ */ +/* $Author: tknoll $ */ + +/** \file + * Error code values. + */ + +/*****************************************************************************/ + +#ifndef __dng_errors__ +#define __dng_errors__ + +/*****************************************************************************/ + +#include "dng_types.h" + +/*****************************************************************************/ + +/// Type for all errors used in DNG SDK. Generally held inside a dng_exception. + +typedef int32 dng_error_code; + +enum + { + dng_error_none = 0, //!< No error. Success. + dng_error_unknown = 100000, //!< Logic or program error or other unclassifiable error. + dng_error_not_yet_implemented, //!< Functionality requested is not yet implemented. + dng_error_silent, //!< An error which should not be signalled to user. + dng_error_user_canceled, //!< Processing stopped by user (or host application) request + dng_error_host_insufficient, //!< Necessary host functionality is not present. + dng_error_memory, //!< Out of memory. + dng_error_bad_format, //!< File format is not valid. + dng_error_matrix_math, //!< Matrix has wrong shape, is badly conditioned, or similar problem. + dng_error_open_file, //!< Could not open file. + dng_error_read_file, //!< Error reading file. + dng_error_write_file, //!< Error writing file. + dng_error_end_of_file, //!< Unexpected end of file. + dng_error_file_is_damaged, //!< File is damaged in some way. + dng_error_image_too_big_dng, //!< Image is too big to save as DNG. + dng_error_image_too_big_tiff, //!< Image is too big to save as TIFF. + dng_error_unsupported_dng //!< DNG version is unsupported. + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_exceptions.cpp b/source/lib/dng_sdk/dng_exceptions.cpp new file mode 100644 index 0000000..6ca4ece --- /dev/null +++ b/source/lib/dng_sdk/dng_exceptions.cpp @@ -0,0 +1,205 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_exceptions.cpp#2 $ */ +/* $DateTime: 2012/06/06 12:08:58 $ */ +/* $Change: 833617 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_exceptions.h" + +#include "dng_flags.h" +#include "dng_globals.h" + +/*****************************************************************************/ + +#ifndef qDNGReportErrors +// assuming this isn't enable on Win, because it's using printf, but an app can redirect that to console +#define qDNGReportErrors ((qDNGDebug && qMacOS) || qDNGValidate) +#endif + +/*****************************************************************************/ + +void ReportWarning (const char *message, + const char *sub_message) + { + + + #if qDNGReportErrors + + if (sub_message) + fprintf (stderr, "*** Warning: %s (%s) ***\n", message, sub_message); + else + fprintf (stderr, "*** Warning: %s ***\n", message); + + #else + + (void) message; + (void) sub_message; + + #endif + + } + +/*****************************************************************************/ + +void ReportError (const char *message, + const char *sub_message) + { + + #if qDNGReportErrors + + if (sub_message) + fprintf (stderr, "*** Error: %s (%s) ***\n", message, sub_message); + else + fprintf (stderr, "*** Error: %s ***\n", message); + + #else + + (void) message; + (void) sub_message; + + #endif + + } + +/*****************************************************************************/ + +void Throw_dng_error (dng_error_code err, + const char *message, + const char *sub_message, + bool silent) + { + + #if qDNGReportErrors + + { + + if (!message) + { + + switch (err) + { + + case dng_error_none: + case dng_error_silent: + case dng_error_user_canceled: + { + break; + } + + case dng_error_not_yet_implemented: + { + message = "Not yet implemented"; + break; + } + + case dng_error_host_insufficient: + { + message = "Host insufficient"; + break; + } + + case dng_error_memory: + { + message = "Unable to allocate memory"; + break; + } + + case dng_error_bad_format: + { + message = "File format is invalid"; + break; + } + + case dng_error_matrix_math: + { + message = "Matrix math error"; + break; + } + + case dng_error_open_file: + { + message = "Unable to open file"; + break; + } + + case dng_error_read_file: + { + message = "File read error"; + break; + } + + case dng_error_write_file: + { + message = "File write error"; + break; + } + + case dng_error_end_of_file: + { + message = "Unexpected end-of-file"; + break; + } + + case dng_error_file_is_damaged: + { + message = "File is damaged"; + break; + } + + case dng_error_image_too_big_dng: + { + message = "Image is too big to save as DNG"; + break; + } + + case dng_error_image_too_big_tiff: + { + message = "Image is too big to save as TIFF"; + break; + } + + case dng_error_unsupported_dng: + { + message = "DNG version is unsupported"; + break; + } + + default: + { + message = "Unknown error"; + break; + } + + } + + } + + if (message && !silent) + { + ReportError (message, sub_message); + } + + } + + #else + + (void) message; + (void) sub_message; + (void) silent; + + #endif + + throw dng_exception (err); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_exceptions.h b/source/lib/dng_sdk/dng_exceptions.h new file mode 100644 index 0000000..7bfb1cd --- /dev/null +++ b/source/lib/dng_sdk/dng_exceptions.h @@ -0,0 +1,301 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_exceptions.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * C++ exception support for DNG SDK. +*/ + +/*****************************************************************************/ + +#ifndef __dng_exceptions__ +#define __dng_exceptions__ + +/*****************************************************************************/ + +#include "dng_errors.h" +#include "dng_flags.h" + +/*****************************************************************************/ + +/// Display a warning message. Note that this may just eat the message. + +void ReportWarning (const char *message, + const char *sub_message = NULL); + +/*****************************************************************************/ + +/// Display an error message. Note that this may just eat the message. + +void ReportError (const char *message, + const char *sub_message = NULL); + +/*****************************************************************************/ + +/// \brief All exceptions thrown by the DNG SDK use this exception class. + +class dng_exception + { + + private: + + dng_error_code fErrorCode; + + public: + + /// Construct an exception representing the given error code. + /// \param code Error code this exception is for. + + dng_exception (dng_error_code code) + + : fErrorCode (code) + + { + } + + virtual ~dng_exception () + { + } + + /// Getter for error code of this exception + /// \retval The error code of this exception. + + dng_error_code ErrorCode () const + { + return fErrorCode; + } + + }; + +/******************************************************************************/ + +/// \brief Throw an exception based on an arbitrary error code. + +void Throw_dng_error (dng_error_code err, + const char * message = NULL, + const char * sub_message = NULL, + bool silent = false); + +/******************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code if +/// error_code is not dng_error_none . + +inline void Fail_dng_error (dng_error_code err) + { + + if (err != dng_error_none) + { + + Throw_dng_error (err); + + } + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_unknown . + +inline void ThrowProgramError (const char * sub_message = NULL) + { + + Throw_dng_error (dng_error_unknown, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_not_yet_implemented . + +inline void ThrowNotYetImplemented (const char * sub_message = NULL) + { + + Throw_dng_error (dng_error_not_yet_implemented, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_silent . + +inline void ThrowSilentError () + { + + Throw_dng_error (dng_error_silent); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_user_canceled . + +inline void ThrowUserCanceled () + { + + Throw_dng_error (dng_error_user_canceled); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_host_insufficient . + +inline void ThrowHostInsufficient (const char * sub_message = NULL) + { + + Throw_dng_error (dng_error_host_insufficient, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_memory . + +inline void ThrowMemoryFull (const char * sub_message = NULL) + { + + Throw_dng_error (dng_error_memory, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_bad_format . + +inline void ThrowBadFormat (const char * sub_message = NULL) + { + + Throw_dng_error (dng_error_bad_format, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_matrix_math . + +inline void ThrowMatrixMath (const char * sub_message = NULL) + { + + Throw_dng_error (dng_error_matrix_math, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_open_file . + +inline void ThrowOpenFile (const char * sub_message = NULL, bool silent = false) + { + + Throw_dng_error (dng_error_open_file, NULL, sub_message, silent); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_read_file . + +inline void ThrowReadFile (const char *sub_message = NULL) + { + + Throw_dng_error (dng_error_read_file, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_write_file . + +inline void ThrowWriteFile (const char *sub_message = NULL) + { + + Throw_dng_error (dng_error_write_file, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_end_of_file . + +inline void ThrowEndOfFile (const char *sub_message = NULL) + { + + Throw_dng_error (dng_error_end_of_file, NULL, sub_message); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_file_is_damaged . + +inline void ThrowFileIsDamaged () + { + + Throw_dng_error (dng_error_file_is_damaged); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_image_too_big_dng . + +inline void ThrowImageTooBigDNG () + { + + Throw_dng_error (dng_error_image_too_big_dng); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_image_too_big_tiff . + +inline void ThrowImageTooBigTIFF () + { + + Throw_dng_error (dng_error_image_too_big_tiff); + + } + +/*****************************************************************************/ + +/// \brief Convenience function to throw dng_exception with error code +/// dng_error_unsupported_dng . + +inline void ThrowUnsupportedDNG () + { + + Throw_dng_error (dng_error_unsupported_dng); + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_exif.cpp b/source/lib/dng_sdk/dng_exif.cpp new file mode 100644 index 0000000..294557d --- /dev/null +++ b/source/lib/dng_sdk/dng_exif.cpp @@ -0,0 +1,4381 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_exif.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_exif.h" + +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_parse_utils.h" +#include "dng_globals.h" +#include "dng_exceptions.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_exif::dng_exif () + + : fImageDescription () + , fMake () + , fModel () + , fSoftware () + , fArtist () + , fCopyright () + , fCopyright2 () + , fUserComment () + + , fDateTime () + , fDateTimeStorageInfo () + + , fDateTimeOriginal () + , fDateTimeOriginalStorageInfo () + + , fDateTimeDigitized () + , fDateTimeDigitizedStorageInfo () + + , fTIFF_EP_StandardID (0) + , fExifVersion (0) + , fFlashPixVersion (0) + + , fExposureTime () + , fFNumber () + , fShutterSpeedValue () + , fApertureValue () + , fBrightnessValue () + , fExposureBiasValue () + , fMaxApertureValue () + , fFocalLength () + , fDigitalZoomRatio () + , fExposureIndex () + , fSubjectDistance () + , fGamma () + + , fBatteryLevelR () + , fBatteryLevelA () + + , fExposureProgram (0xFFFFFFFF) + , fMeteringMode (0xFFFFFFFF) + , fLightSource (0xFFFFFFFF) + , fFlash (0xFFFFFFFF) + , fFlashMask (0x0000FFFF) + , fSensingMethod (0xFFFFFFFF) + , fColorSpace (0xFFFFFFFF) + , fFileSource (0xFFFFFFFF) + , fSceneType (0xFFFFFFFF) + , fCustomRendered (0xFFFFFFFF) + , fExposureMode (0xFFFFFFFF) + , fWhiteBalance (0xFFFFFFFF) + , fSceneCaptureType (0xFFFFFFFF) + , fGainControl (0xFFFFFFFF) + , fContrast (0xFFFFFFFF) + , fSaturation (0xFFFFFFFF) + , fSharpness (0xFFFFFFFF) + , fSubjectDistanceRange (0xFFFFFFFF) + , fSelfTimerMode (0xFFFFFFFF) + , fImageNumber (0xFFFFFFFF) + + , fFocalLengthIn35mmFilm (0) + + , fSensitivityType (0) + , fStandardOutputSensitivity (0) + , fRecommendedExposureIndex (0) + , fISOSpeed (0) + , fISOSpeedLatitudeyyy (0) + , fISOSpeedLatitudezzz (0) + + , fSubjectAreaCount (0) + + , fComponentsConfiguration (0) + + , fCompresssedBitsPerPixel () + + , fPixelXDimension (0) + , fPixelYDimension (0) + + , fFocalPlaneXResolution () + , fFocalPlaneYResolution () + + , fFocalPlaneResolutionUnit (0xFFFFFFFF) + + , fCFARepeatPatternRows (0) + , fCFARepeatPatternCols (0) + + , fImageUniqueID () + + , fGPSVersionID (0) + , fGPSLatitudeRef () + , fGPSLongitudeRef () + , fGPSAltitudeRef (0xFFFFFFFF) + , fGPSAltitude () + , fGPSSatellites () + , fGPSStatus () + , fGPSMeasureMode () + , fGPSDOP () + , fGPSSpeedRef () + , fGPSSpeed () + , fGPSTrackRef () + , fGPSTrack () + , fGPSImgDirectionRef () + , fGPSImgDirection () + , fGPSMapDatum () + , fGPSDestLatitudeRef () + , fGPSDestLongitudeRef () + , fGPSDestBearingRef () + , fGPSDestBearing () + , fGPSDestDistanceRef () + , fGPSDestDistance () + , fGPSProcessingMethod () + , fGPSAreaInformation () + , fGPSDateStamp () + , fGPSDifferential (0xFFFFFFFF) + , fGPSHPositioningError () + + , fInteroperabilityIndex () + + , fInteroperabilityVersion (0) + + , fRelatedImageFileFormat () + + , fRelatedImageWidth (0) + , fRelatedImageLength (0) + + , fCameraSerialNumber () + + , fLensID () + , fLensMake () + , fLensName () + , fLensSerialNumber () + + , fLensNameWasReadFromExif (false) + + , fApproxFocusDistance () + + , fFlashCompensation () + + , fOwnerName () + , fFirmware () + + { + + uint32 j; + uint32 k; + + fISOSpeedRatings [0] = 0; + fISOSpeedRatings [1] = 0; + fISOSpeedRatings [2] = 0; + + for (j = 0; j < kMaxCFAPattern; j++) + for (k = 0; k < kMaxCFAPattern; k++) + { + fCFAPattern [j] [k] = 255; + } + + } + +/*****************************************************************************/ + +dng_exif::~dng_exif () + { + + } + +/*****************************************************************************/ + +dng_exif * dng_exif::Clone () const + { + + dng_exif *result = new dng_exif (*this); + + if (!result) + { + ThrowMemoryFull (); + } + + return result; + + } + +/*****************************************************************************/ + +void dng_exif::SetEmpty () + { + + *this = dng_exif (); + + } + +/*****************************************************************************/ + +void dng_exif::CopyGPSFrom (const dng_exif &exif) + { + + fGPSVersionID = exif.fGPSVersionID; + fGPSLatitudeRef = exif.fGPSLatitudeRef; + fGPSLatitude [0] = exif.fGPSLatitude [0]; + fGPSLatitude [1] = exif.fGPSLatitude [1]; + fGPSLatitude [2] = exif.fGPSLatitude [2]; + fGPSLongitudeRef = exif.fGPSLongitudeRef; + fGPSLongitude [0] = exif.fGPSLongitude [0]; + fGPSLongitude [1] = exif.fGPSLongitude [1]; + fGPSLongitude [2] = exif.fGPSLongitude [2]; + fGPSAltitudeRef = exif.fGPSAltitudeRef; + fGPSAltitude = exif.fGPSAltitude; + fGPSTimeStamp [0] = exif.fGPSTimeStamp [0]; + fGPSTimeStamp [1] = exif.fGPSTimeStamp [1]; + fGPSTimeStamp [2] = exif.fGPSTimeStamp [2]; + fGPSSatellites = exif.fGPSSatellites; + fGPSStatus = exif.fGPSStatus; + fGPSMeasureMode = exif.fGPSMeasureMode; + fGPSDOP = exif.fGPSDOP; + fGPSSpeedRef = exif.fGPSSpeedRef; + fGPSSpeed = exif.fGPSSpeed; + fGPSTrackRef = exif.fGPSTrackRef; + fGPSTrack = exif.fGPSTrack; + fGPSImgDirectionRef = exif.fGPSImgDirectionRef; + fGPSImgDirection = exif.fGPSImgDirection; + fGPSMapDatum = exif.fGPSMapDatum; + fGPSDestLatitudeRef = exif.fGPSDestLatitudeRef; + fGPSDestLatitude [0] = exif.fGPSDestLatitude [0]; + fGPSDestLatitude [1] = exif.fGPSDestLatitude [1]; + fGPSDestLatitude [2] = exif.fGPSDestLatitude [2]; + fGPSDestLongitudeRef = exif.fGPSDestLongitudeRef; + fGPSDestLongitude [0] = exif.fGPSDestLongitude [0]; + fGPSDestLongitude [1] = exif.fGPSDestLongitude [1]; + fGPSDestLongitude [2] = exif.fGPSDestLongitude [2]; + fGPSDestBearingRef = exif.fGPSDestBearingRef; + fGPSDestBearing = exif.fGPSDestBearing; + fGPSDestDistanceRef = exif.fGPSDestDistanceRef; + fGPSDestDistance = exif.fGPSDestDistance; + fGPSProcessingMethod = exif.fGPSProcessingMethod; + fGPSAreaInformation = exif.fGPSAreaInformation; + fGPSDateStamp = exif.fGPSDateStamp; + fGPSDifferential = exif.fGPSDifferential; + fGPSHPositioningError = exif.fGPSHPositioningError; + + } + +/*****************************************************************************/ + +// Fix up common errors and rounding issues with EXIF exposure times. + +real64 dng_exif::SnapExposureTime (real64 et) + { + + // Protection against invalid values. + + if (et <= 0.0) + return 0.0; + + // If near a standard shutter speed, snap to it. + + static const real64 kStandardSpeed [] = + { + 30.0, + 25.0, + 20.0, + 15.0, + 13.0, + 10.0, + 8.0, + 6.0, + 5.0, + 4.0, + 3.2, + 3.0, + 2.5, + 2.0, + 1.6, + 1.5, + 1.3, + 1.0, + 0.8, + 0.7, + 0.6, + 0.5, + 0.4, + 0.3, + 1.0 / 4.0, + 1.0 / 5.0, + 1.0 / 6.0, + 1.0 / 8.0, + 1.0 / 10.0, + 1.0 / 13.0, + 1.0 / 15.0, + 1.0 / 20.0, + 1.0 / 25.0, + 1.0 / 30.0, + 1.0 / 40.0, + 1.0 / 45.0, + 1.0 / 50.0, + 1.0 / 60.0, + 1.0 / 80.0, + 1.0 / 90.0, + 1.0 / 100.0, + 1.0 / 125.0, + 1.0 / 160.0, + 1.0 / 180.0, + 1.0 / 200.0, + 1.0 / 250.0, + 1.0 / 320.0, + 1.0 / 350.0, + 1.0 / 400.0, + 1.0 / 500.0, + 1.0 / 640.0, + 1.0 / 750.0, + 1.0 / 800.0, + 1.0 / 1000.0, + 1.0 / 1250.0, + 1.0 / 1500.0, + 1.0 / 1600.0, + 1.0 / 2000.0, + 1.0 / 2500.0, + 1.0 / 3000.0, + 1.0 / 3200.0, + 1.0 / 4000.0, + 1.0 / 5000.0, + 1.0 / 6000.0, + 1.0 / 6400.0, + 1.0 / 8000.0, + 1.0 / 10000.0, + 1.0 / 12000.0, + 1.0 / 12800.0, + 1.0 / 16000.0 + }; + + uint32 count = sizeof (kStandardSpeed ) / + sizeof (kStandardSpeed [0]); + + for (uint32 fudge = 0; fudge <= 1; fudge++) + { + + real64 testSpeed = et; + + if (fudge == 1) + { + + // Often APEX values are rounded to a power of two, + // which results in non-standard shutter speeds. + + if (et >= 0.1) + { + + // No fudging slower than 1/10 second + + break; + + } + + else if (et >= 0.01) + { + + // Between 1/10 and 1/100 the commonly misrounded + // speeds are 1/15, 1/30, 1/60, which are often encoded as + // 1/16, 1/32, 1/64. Try fudging and see if we get + // near a standard speed. + + testSpeed *= 16.0 / 15.0; + + } + + else + { + + // Faster than 1/100, the commonly misrounded + // speeds are 1/125, 1/250, 1/500, etc., which + // are often encoded as 1/128, 1/256, 1/512. + + testSpeed *= 128.0 / 125.0; + + } + + } + + for (uint32 index = 0; index < count; index++) + { + + if (testSpeed >= kStandardSpeed [index] * 0.98 && + testSpeed <= kStandardSpeed [index] * 1.02) + { + + return kStandardSpeed [index]; + + } + + } + + } + + // We are not near any standard speeds. Round the non-standard value to something + // that looks reasonable. + + if (et >= 10.0) + { + + // Round to nearest second. + + et = floor (et + 0.5); + + } + + else if (et >= 0.5) + { + + // Round to nearest 1/10 second + + et = floor (et * 10.0 + 0.5) * 0.1; + + } + + else if (et >= 1.0 / 20.0) + { + + // Round to an exact inverse. + + et = 1.0 / floor (1.0 / et + 0.5); + + } + + else if (et >= 1.0 / 130.0) + { + + // Round inverse to multiple of 5 + + et = 0.2 / floor (0.2 / et + 0.5); + + } + + else if (et >= 1.0 / 750.0) + { + + // Round inverse to multiple of 10 + + et = 0.1 / floor (0.1 / et + 0.5); + + } + + else if (et >= 1.0 / 1300.0) + { + + // Round inverse to multiple of 50 + + et = 0.02 / floor (0.02 / et + 0.5); + + } + + else if (et >= 1.0 / 15000.0) + { + + // Round inverse to multiple of 100 + + et = 0.01 / floor (0.01 / et + 0.5); + + } + + else + { + + // Round inverse to multiple of 1000 + + et = 0.001 / floor (0.001 / et + 0.5); + + } + + return et; + + } + +/*****************************************************************************/ + +void dng_exif::SetExposureTime (real64 et, bool snap) + { + + fExposureTime.Clear (); + + fShutterSpeedValue.Clear (); + + if (snap) + { + + et = SnapExposureTime (et); + + } + + if (et >= 1.0 / 32768.0 && et <= 32768.0) + { + + if (et >= 100.0) + { + + fExposureTime.Set_real64 (et, 1); + + } + + else if (et >= 1.0) + { + + fExposureTime.Set_real64 (et, 10); + + fExposureTime.ReduceByFactor (10); + + } + + else if (et <= 0.1) + { + + fExposureTime = dng_urational (1, Round_uint32 (1.0 / et)); + + } + + else + { + + fExposureTime.Set_real64 (et, 100); + + fExposureTime.ReduceByFactor (10); + + for (uint32 f = 2; f <= 9; f++) + { + + real64 z = 1.0 / (real64) f / et; + + if (z >= 0.99 && z <= 1.01) + { + + fExposureTime = dng_urational (1, f); + + break; + + } + + } + + } + + // Now mirror this value to the ShutterSpeedValue field. + + et = fExposureTime.As_real64 (); + + fShutterSpeedValue.Set_real64 (-log (et) / log (2.0), 1000000); + + fShutterSpeedValue.ReduceByFactor (10); + fShutterSpeedValue.ReduceByFactor (10); + fShutterSpeedValue.ReduceByFactor (10); + fShutterSpeedValue.ReduceByFactor (10); + fShutterSpeedValue.ReduceByFactor (10); + fShutterSpeedValue.ReduceByFactor (10); + + } + + } + +/*****************************************************************************/ + +void dng_exif::SetShutterSpeedValue (real64 ss) + { + + if (fExposureTime.NotValid ()) + { + + real64 et = pow (2.0, -ss); + + SetExposureTime (et, true); + + } + + } + +/******************************************************************************/ + +dng_urational dng_exif::EncodeFNumber (real64 fs) + { + + dng_urational y; + + if (fs > 10.0) + { + + y.Set_real64 (fs, 1); + + } + + else if (fs < 1.0) + { + + y.Set_real64 (fs, 100); + + y.ReduceByFactor (10); + y.ReduceByFactor (10); + + } + + else + { + + y.Set_real64 (fs, 10); + + y.ReduceByFactor (10); + + } + + return y; + + } + +/*****************************************************************************/ + +void dng_exif::SetFNumber (real64 fs) + { + + fFNumber.Clear (); + + fApertureValue.Clear (); + + // Allow f-number values less than 1.0 (e.g., f/0.95), even though they would + // correspond to negative APEX values, which the EXIF specification does not + // support (ApertureValue is a rational, not srational). The ApertureValue tag + // will be omitted in the case where fs < 1.0. + + if (fs > 0.0 && fs <= 32768.0) + { + + fFNumber = EncodeFNumber (fs); + + // Now mirror this value to the ApertureValue field. + + real64 av = FNumberToApertureValue (fFNumber); + + if (av >= 0.0 && av <= 99.99) + { + + fApertureValue.Set_real64 (av, 1000000); + + fApertureValue.ReduceByFactor (10); + fApertureValue.ReduceByFactor (10); + fApertureValue.ReduceByFactor (10); + fApertureValue.ReduceByFactor (10); + fApertureValue.ReduceByFactor (10); + fApertureValue.ReduceByFactor (10); + + } + + } + + } + +/*****************************************************************************/ + +void dng_exif::SetApertureValue (real64 av) + { + + if (fFNumber.NotValid ()) + { + + SetFNumber (ApertureValueToFNumber (av)); + + } + + } + +/*****************************************************************************/ + +real64 dng_exif::ApertureValueToFNumber (real64 av) + { + + return pow (2.0, 0.5 * av); + + } + +/*****************************************************************************/ + +real64 dng_exif::ApertureValueToFNumber (const dng_urational &av) + { + + return ApertureValueToFNumber (av.As_real64 ()); + + } + +/*****************************************************************************/ + +real64 dng_exif::FNumberToApertureValue (real64 fNumber) + { + + return 2.0 * log (fNumber) / log (2.0); + + } + +/*****************************************************************************/ + +real64 dng_exif::FNumberToApertureValue (const dng_urational &fNumber) + { + + return FNumberToApertureValue (fNumber.As_real64 ()); + + } + +/*****************************************************************************/ + +void dng_exif::UpdateDateTime (const dng_date_time_info &dt) + { + + fDateTime = dt; + + } + +/*****************************************************************************/ + +bool dng_exif::AtLeastVersion0230 () const + { + + uint32 b0 = (fExifVersion >> 24) & 0xff; + uint32 b1 = (fExifVersion >> 16) & 0xff; + uint32 b2 = (fExifVersion >> 8) & 0xff; + + return (b0 > 0) || (b1 >= 2 && b2 >= 3); + + } + +/*****************************************************************************/ + +bool dng_exif::ParseTag (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + bool isMainIFD, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset) + { + + if (parentCode == 0) + { + + if (Parse_ifd0 (stream, + shared, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + if (parentCode == 0 || isMainIFD) + { + + if (Parse_ifd0_main (stream, + shared, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + if (parentCode == 0 || + parentCode == tcExifIFD) + { + + if (Parse_ifd0_exif (stream, + shared, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + if (parentCode == tcGPSInfo) + { + + if (Parse_gps (stream, + shared, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + if (parentCode == tcInteroperabilityIFD) + { + + if (Parse_interoperability (stream, + shared, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in IFD 0. + +bool dng_exif::Parse_ifd0 (dng_stream &stream, + dng_shared & /* shared */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 /* tagOffset */) + { + + switch (tagCode) + { + + case tcImageDescription: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fImageDescription); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ImageDescription: "); + + DumpString (fImageDescription); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcMake: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fMake); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Make: "); + + DumpString (fMake); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcModel: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fModel); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Model: "); + + DumpString (fModel); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcSoftware: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fSoftware); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Software: "); + + DumpString (fSoftware); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcDateTime: + { + + uint64 tagPosition = stream.PositionInOriginalFile (); + + dng_date_time dt; + + if (!ParseDateTimeTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + dt)) + { + return false; + } + + fDateTime.SetDateTime (dt); + + fDateTimeStorageInfo = dng_date_time_storage_info (tagPosition, + dng_date_time_format_exif); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DateTime: "); + + DumpDateTime (fDateTime.DateTime ()); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcArtist: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fArtist); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Artist: "); + + DumpString (fArtist); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcCopyright: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseDualStringTag (stream, + parentCode, + tagCode, + tagCount, + fCopyright, + fCopyright2); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Copyright: "); + + DumpString (fCopyright); + + if (fCopyright2.Get () [0] != 0) + { + + printf (" "); + + DumpString (fCopyright2); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcTIFF_EP_StandardID: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fTIFF_EP_StandardID = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + printf ("TIFF/EPStandardID: %u.%u.%u.%u\n", + (unsigned) b0, + (unsigned) b1, + (unsigned) b2, + (unsigned) b3); + } + + #endif + + break; + + } + + case tcCameraSerialNumber: + case tcKodakCameraSerialNumber: // Kodak uses a very similar tag. + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fCameraSerialNumber); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (fCameraSerialNumber); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcLensInfo: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 4)) + return false; + + fLensInfo [0] = stream.TagValue_urational (tagType); + fLensInfo [1] = stream.TagValue_urational (tagType); + fLensInfo [2] = stream.TagValue_urational (tagType); + fLensInfo [3] = stream.TagValue_urational (tagType); + + // Some third party software wrote zero rather and undefined values + // for unknown entries. Work around this bug. + + for (uint32 j = 0; j < 4; j++) + { + + if (fLensInfo [j].IsValid () && fLensInfo [j].As_real64 () <= 0.0) + { + + fLensInfo [j] = dng_urational (0, 0); + + #if qDNGValidate + + ReportWarning ("Zero entry in LensInfo tag--should be undefined"); + + #endif + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("LensInfo: "); + + real64 minFL = fLensInfo [0].As_real64 (); + real64 maxFL = fLensInfo [1].As_real64 (); + + if (minFL == maxFL) + printf ("%0.1f mm", minFL); + else + printf ("%0.1f-%0.1f mm", minFL, maxFL); + + if (fLensInfo [2].d) + { + + real64 minFS = fLensInfo [2].As_real64 (); + real64 maxFS = fLensInfo [3].As_real64 (); + + if (minFS == maxFS) + printf (" f/%0.1f", minFS); + else + printf (" f/%0.1f-%0.1f", minFS, maxFS); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in IFD 0 or the main image IFD. + +bool dng_exif::Parse_ifd0_main (dng_stream &stream, + dng_shared & /* shared */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 /* tagOffset */) + { + + switch (tagCode) + { + + case tcFocalPlaneXResolution: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalPlaneXResolution = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalPlaneXResolution: %0.4f\n", + fFocalPlaneXResolution.As_real64 ()); + + } + + #endif + + break; + + } + + case tcFocalPlaneYResolution: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalPlaneYResolution = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalPlaneYResolution: %0.4f\n", + fFocalPlaneYResolution.As_real64 ()); + + } + + #endif + + break; + + } + + case tcFocalPlaneResolutionUnit: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalPlaneResolutionUnit = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalPlaneResolutionUnit: %s\n", + LookupResolutionUnit (fFocalPlaneResolutionUnit)); + + } + + #endif + + break; + + } + + case tcSensingMethod: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSensingMethod = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SensingMethod: %s\n", + LookupSensingMethod (fSensingMethod)); + + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in IFD 0 or EXIF IFD. + +bool dng_exif::Parse_ifd0_exif (dng_stream &stream, + dng_shared & /* shared */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 /* tagOffset */) + { + + switch (tagCode) + { + + case tcBatteryLevel: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational, ttAscii); + + if (tagType == ttAscii) + { + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fBatteryLevelA); + + } + + else + { + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBatteryLevelR = stream.TagValue_urational (tagType); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BatteryLevel: "); + + if (tagType == ttAscii) + { + + DumpString (fBatteryLevelA); + + } + + else + { + + printf ("%0.2f", fBatteryLevelR.As_real64 ()); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcExposureTime: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + dng_urational et = stream.TagValue_urational (tagType); + + SetExposureTime (et.As_real64 (), false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ExposureTime: "); + + DumpExposureTime (et.As_real64 ()); + + printf ("\n"); + + } + + if (et.As_real64 () <= 0.0) + { + + ReportWarning ("The ExposureTime is <= 0"); + + } + + #endif + + break; + + } + + case tcFNumber: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + dng_urational fs = stream.TagValue_urational (tagType); + + // Sometimes "unknown" is recorded as zero. + + if (fs.As_real64 () <= 0.0) + { + fs.Clear (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FNumber: f/%0.2f\n", + fs.As_real64 ()); + + } + + #endif + + SetFNumber (fs.As_real64 ()); + + break; + + } + + case tcExposureProgram: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fExposureProgram = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ExposureProgram: %s\n", + LookupExposureProgram (fExposureProgram)); + + } + + #endif + + break; + + } + + case tcISOSpeedRatings: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1, 3); + + for (uint32 j = 0; j < tagCount && j < 3; j++) + { + + fISOSpeedRatings [j] = stream.TagValue_uint32 (tagType); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ISOSpeedRatings:"); + + for (uint32 j = 0; j < tagCount && j < 3; j++) + { + + printf (" %u", (unsigned) fISOSpeedRatings [j]); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcSensitivityType: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSensitivityType = (uint32) stream.Get_uint16 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SensitivityType: %s\n", + LookupSensitivityType (fSensitivityType)); + + } + + #endif + + break; + + } + + case tcStandardOutputSensitivity: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fStandardOutputSensitivity = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("StandardOutputSensitivity: %u\n", + (unsigned) fStandardOutputSensitivity); + + } + + #endif + + break; + + } + + case tcRecommendedExposureIndex: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fRecommendedExposureIndex = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("RecommendedExposureIndex: %u\n", + (unsigned) fRecommendedExposureIndex); + + } + + #endif + + break; + + } + + case tcISOSpeed: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fISOSpeed = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ISOSpeed: %u\n", + (unsigned) fISOSpeed); + + } + + #endif + + break; + + } + + case tcISOSpeedLatitudeyyy: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fISOSpeedLatitudeyyy = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ISOSpeedLatitudeyyy: %u\n", + (unsigned) fISOSpeedLatitudeyyy); + + } + + #endif + + break; + + } + + case tcISOSpeedLatitudezzz: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fISOSpeedLatitudezzz = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ISOSpeedLatitudezzz: %u\n", + (unsigned) fISOSpeedLatitudezzz); + + } + + #endif + + break; + + } + + case tcTimeZoneOffset: + { + + CheckTagType (parentCode, tagCode, tagType, ttSShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1, 2); + + dng_time_zone zoneOriginal; + + zoneOriginal.SetOffsetHours (stream.TagValue_int32 (tagType)); + + fDateTimeOriginal.SetZone (zoneOriginal); + + dng_time_zone zoneCurrent; + + if (tagCount >= 2) + { + + zoneCurrent.SetOffsetHours (stream.TagValue_int32 (tagType)); + + fDateTime.SetZone (zoneCurrent); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("TimeZoneOffset: DateTimeOriginal = %d", + (int) zoneOriginal.ExactHourOffset ()); + + if (tagCount >= 2) + { + + printf (", DateTime = %d", + (int) zoneCurrent.ExactHourOffset ()); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcSelfTimerMode: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSelfTimerMode = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SelfTimerMode: "); + + if (fSelfTimerMode) + { + + printf ("%u sec", (unsigned) fSelfTimerMode); + + } + + else + { + + printf ("Off"); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcExifVersion: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fExifVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + + real64 x = (b0 - '0') * 10.00 + + (b1 - '0') * 1.00 + + (b2 - '0') * 0.10 + + (b3 - '0') * 0.01; + + printf ("ExifVersion: %0.2f\n", x); + + } + + #endif + + break; + + } + + case tcDateTimeOriginal: + { + + uint64 tagPosition = stream.PositionInOriginalFile (); + + dng_date_time dt; + + if (!ParseDateTimeTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + dt)) + { + return false; + } + + fDateTimeOriginal.SetDateTime (dt); + + fDateTimeOriginalStorageInfo = dng_date_time_storage_info (tagPosition, + dng_date_time_format_exif); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DateTimeOriginal: "); + + DumpDateTime (fDateTimeOriginal.DateTime ()); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcDateTimeDigitized: + { + + uint64 tagPosition = stream.PositionInOriginalFile (); + + dng_date_time dt; + + if (!ParseDateTimeTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + dt)) + { + return false; + } + + fDateTimeDigitized.SetDateTime (dt); + + fDateTimeDigitizedStorageInfo = dng_date_time_storage_info (tagPosition, + dng_date_time_format_exif); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DateTimeDigitized: "); + + DumpDateTime (fDateTimeDigitized.DateTime ()); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcComponentsConfiguration: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fComponentsConfiguration = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ComponentsConfiguration: %s %s %s %s\n", + LookupComponent (b0), + LookupComponent (b1), + LookupComponent (b2), + LookupComponent (b3)); + + } + + #endif + + break; + + } + + case tcCompressedBitsPerPixel: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCompresssedBitsPerPixel = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CompressedBitsPerPixel: %0.2f\n", + fCompresssedBitsPerPixel.As_real64 ()); + + } + + #endif + + break; + + } + + case tcShutterSpeedValue: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + dng_srational ss = stream.TagValue_srational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ShutterSpeedValue: "); + + real64 x = pow (2.0, -ss.As_real64 ()); + + DumpExposureTime (x); + + printf ("\n"); + + } + + // The ExposureTime and ShutterSpeedValue tags should be consistent. + + if (fExposureTime.IsValid ()) + { + + real64 et = fExposureTime.As_real64 (); + + real64 tv1 = -1.0 * log (et) / log (2.0); + + real64 tv2 = ss.As_real64 (); + + // Make sure they are within 0.25 APEX values. + + if (Abs_real64 (tv1 - tv2) > 0.25) + { + + ReportWarning ("The ExposureTime and ShutterSpeedValue tags have conflicting values"); + + } + + } + + #endif + + SetShutterSpeedValue (ss.As_real64 ()); + + break; + + } + + case tcApertureValue: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + dng_urational av = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + real64 x = pow (2.0, 0.5 * av.As_real64 ()); + + printf ("ApertureValue: f/%0.2f\n", x); + + } + + // The FNumber and ApertureValue tags should be consistent. + + if (fFNumber.IsValid () && av.IsValid ()) + { + + real64 fs = fFNumber.As_real64 (); + + real64 av1 = FNumberToApertureValue (fs); + + real64 av2 = av.As_real64 (); + + if (Abs_real64 (av1 - av2) > 0.25) + { + + ReportWarning ("The FNumber and ApertureValue tags have conflicting values"); + + } + + } + + #endif + + SetApertureValue (av.As_real64 ()); + + break; + + } + + case tcBrightnessValue: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBrightnessValue = stream.TagValue_srational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BrightnessValue: %0.2f\n", + fBrightnessValue.As_real64 ()); + + } + + #endif + + break; + + } + + case tcExposureBiasValue: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fExposureBiasValue = stream.TagValue_srational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ExposureBiasValue: %0.2f\n", + fExposureBiasValue.As_real64 ()); + + } + + #endif + + break; + + } + + case tcMaxApertureValue: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fMaxApertureValue = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + real64 x = pow (2.0, 0.5 * fMaxApertureValue.As_real64 ()); + + printf ("MaxApertureValue: f/%0.1f\n", x); + + } + + #endif + + break; + + } + + case tcSubjectDistance: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSubjectDistance = stream.TagValue_urational (tagType); + + fApproxFocusDistance = fSubjectDistance; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SubjectDistance: %u/%u\n", + (unsigned) fSubjectDistance.n, + (unsigned) fSubjectDistance.d); + + } + + #endif + + break; + + } + + case tcMeteringMode: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fMeteringMode = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("MeteringMode: %s\n", + LookupMeteringMode (fMeteringMode)); + + } + + #endif + + break; + + } + + case tcLightSource: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fLightSource = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("LightSource: %s\n", + LookupLightSource (fLightSource)); + + } + + #endif + + break; + + } + + case tcFlash: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFlash = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Flash: %u\n", (unsigned) fFlash); + + if ((fFlash >> 5) & 1) + { + printf (" No flash function\n"); + } + + else + { + + if (fFlash & 0x1) + { + + printf (" Flash fired\n"); + + switch ((fFlash >> 1) & 0x3) + { + + case 2: + printf (" Strobe return light not detected\n"); + break; + + case 3: + printf (" Strobe return light detected\n"); + break; + + } + + } + + else + { + printf (" Flash did not fire\n"); + } + + switch ((fFlash >> 3) & 0x3) + { + + case 1: + printf (" Compulsory flash firing\n"); + break; + + case 2: + printf (" Compulsory flash suppression\n"); + break; + + case 3: + printf (" Auto mode\n"); + break; + + } + + if ((fFlash >> 6) & 1) + { + printf (" Red-eye reduction supported\n"); + } + + } + + } + + #endif + + break; + + } + + case tcFocalLength: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalLength = stream.TagValue_urational (tagType); + + // Sometimes "unknown" is recorded as zero. + + if (fFocalLength.As_real64 () <= 0.0) + { + fFocalLength.Clear (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalLength: %0.1f mm\n", + fFocalLength.As_real64 ()); + + } + + #endif + + break; + + } + + case tcImageNumber: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fImageNumber = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("ImageNumber: %u\n", (unsigned) fImageNumber); + } + + #endif + + break; + + } + + case tcExposureIndex: + case tcExposureIndexExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fExposureIndex = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ISO %0.1f\n", + LookupTagCode (parentCode, tagCode), + fExposureIndex.As_real64 ()); + + } + + #endif + + break; + + } + + case tcUserComment: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + ParseEncodedStringTag (stream, + parentCode, + tagCode, + tagCount, + fUserComment); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("UserComment: "); + + DumpString (fUserComment); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcSubsecTime: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + dng_string subsecs; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + subsecs); + + fDateTime.SetSubseconds (subsecs); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SubsecTime: "); + + DumpString (subsecs); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcSubsecTimeOriginal: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + dng_string subsecs; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + subsecs); + + fDateTimeOriginal.SetSubseconds (subsecs); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SubsecTimeOriginal: "); + + DumpString (subsecs); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcSubsecTimeDigitized: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + dng_string subsecs; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + subsecs); + + fDateTimeDigitized.SetSubseconds (subsecs); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SubsecTimeDigitized: "); + + DumpString (subsecs); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcFlashPixVersion: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fFlashPixVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + + real64 x = (b0 - '0') * 10.00 + + (b1 - '0') * 1.00 + + (b2 - '0') * 0.10 + + (b3 - '0') * 0.01; + + printf ("FlashPixVersion: %0.2f\n", x); + + } + + #endif + + break; + + } + + case tcColorSpace: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fColorSpace = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ColorSpace: %s\n", + LookupColorSpace (fColorSpace)); + + } + + #endif + + break; + + } + + case tcPixelXDimension: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPixelXDimension = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("PixelXDimension: %u\n", (unsigned) fPixelXDimension); + } + + #endif + + break; + + } + + case tcPixelYDimension: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPixelYDimension = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("PixelYDimension: %u\n", (unsigned) fPixelYDimension); + } + + #endif + + break; + + } + + case tcFocalPlaneXResolutionExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalPlaneXResolution = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalPlaneXResolutionExif: %0.4f\n", + fFocalPlaneXResolution.As_real64 ()); + + } + + #endif + + break; + + } + + case tcFocalPlaneYResolutionExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalPlaneYResolution = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalPlaneYResolutionExif: %0.4f\n", + fFocalPlaneYResolution.As_real64 ()); + + } + + #endif + + break; + + } + + case tcFocalPlaneResolutionUnitExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalPlaneResolutionUnit = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalPlaneResolutionUnitExif: %s\n", + LookupResolutionUnit (fFocalPlaneResolutionUnit)); + + } + + #endif + + break; + + } + + case tcSensingMethodExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSensingMethod = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SensingMethodExif: %s\n", + LookupSensingMethod (fSensingMethod)); + + } + + #endif + + break; + + } + + case tcFileSource: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFileSource = stream.Get_uint8 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FileSource: %s\n", + LookupFileSource (fFileSource)); + + } + + #endif + + break; + + } + + case tcSceneType: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSceneType = stream.Get_uint8 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SceneType: %s\n", + LookupSceneType (fSceneType)); + + } + + #endif + + break; + + } + + case tcCFAPatternExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + if (tagCount <= 4) + { + return false; + } + + uint32 cols = stream.Get_uint16 (); + uint32 rows = stream.Get_uint16 (); + + if (tagCount != 4 + cols * rows) + { + return false; + } + + if (cols < 1 || cols > kMaxCFAPattern || + rows < 1 || rows > kMaxCFAPattern) + { + return false; + } + + fCFARepeatPatternCols = cols; + fCFARepeatPatternRows = rows; + + // Note that the Exif spec stores this array in a different + // scan order than the TIFF-EP spec. + + for (uint32 j = 0; j < fCFARepeatPatternCols; j++) + for (uint32 k = 0; k < fCFARepeatPatternRows; k++) + { + + fCFAPattern [k] [j] = stream.Get_uint8 (); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CFAPatternExif:\n"); + + for (uint32 j = 0; j < fCFARepeatPatternRows; j++) + { + + int32 spaces = 4; + + for (uint32 k = 0; k < fCFARepeatPatternCols; k++) + { + + while (spaces-- > 0) + { + printf (" "); + } + + const char *name = LookupCFAColor (fCFAPattern [j] [k]); + + spaces = 9 - (int32) strlen (name); + + printf ("%s", name); + + } + + printf ("\n"); + + } + + } + + #endif + + break; + + } + + case tcCustomRendered: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCustomRendered = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CustomRendered: %s\n", + LookupCustomRendered (fCustomRendered)); + + } + + #endif + + break; + + } + + case tcExposureMode: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fExposureMode = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ExposureMode: %s\n", + LookupExposureMode (fExposureMode)); + + } + + #endif + + break; + + } + + case tcWhiteBalance: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fWhiteBalance = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("WhiteBalance: %s\n", + LookupWhiteBalance (fWhiteBalance)); + + } + + #endif + + break; + + } + + case tcDigitalZoomRatio: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fDigitalZoomRatio = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DigitalZoomRatio: "); + + if (fDigitalZoomRatio.n == 0 || + fDigitalZoomRatio.d == 0) + { + + printf ("Not used\n"); + + } + + else + { + + printf ("%0.2f\n", fDigitalZoomRatio.As_real64 ()); + + } + + } + + #endif + + break; + + } + + case tcFocalLengthIn35mmFilm: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFocalLengthIn35mmFilm = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("FocalLengthIn35mmFilm: %u mm\n", + (unsigned) fFocalLengthIn35mmFilm); + + } + + #endif + + break; + + } + + case tcSceneCaptureType: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSceneCaptureType = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SceneCaptureType: %s\n", + LookupSceneCaptureType (fSceneCaptureType)); + + } + + #endif + + break; + + } + + case tcGainControl: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fGainControl = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("GainControl: %s\n", + LookupGainControl (fGainControl)); + + } + + #endif + + break; + + } + + case tcContrast: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fContrast = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Contrast: %s\n", + LookupContrast (fContrast)); + + } + + #endif + + break; + + } + + case tcSaturation: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSaturation = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Saturation: %s\n", + LookupSaturation (fSaturation)); + + } + + #endif + + break; + + } + + case tcSharpness: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSharpness = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Sharpness: %s\n", + LookupSharpness (fSharpness)); + + } + + #endif + + break; + + } + + case tcSubjectDistanceRange: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSubjectDistanceRange = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SubjectDistanceRange: %s\n", + LookupSubjectDistanceRange (fSubjectDistanceRange)); + + } + + #endif + + break; + + } + + case tcSubjectArea: + case tcSubjectLocation: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2, 4)) + { + return false; + } + + if (tagCode == tcSubjectLocation) + { + CheckTagCount (parentCode, tagCode, tagCount, 2); + } + + fSubjectAreaCount = tagCount; + + for (uint32 j = 0; j < tagCount; j++) + { + + fSubjectArea [j] = stream.TagValue_uint32 (tagType); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s:", LookupTagCode (parentCode, tagCode)); + + for (uint32 j = 0; j < fSubjectAreaCount; j++) + { + + printf (" %u", (unsigned) fSubjectArea [j]); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcGamma: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fGamma = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Gamma: %0.2f\n", + fGamma.As_real64 ()); + + } + + #endif + + break; + + } + + case tcImageUniqueID: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttAscii)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 33)) + return false; + + dng_string s; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + s); + + if (s.Length () != 32) + return false; + + dng_fingerprint f; + + for (uint32 j = 0; j < 32; j++) + { + + char c = ForceUppercase (s.Get () [j]); + + uint32 digit; + + if (c >= '0' && c <= '9') + { + digit = c - '0'; + } + + else if (c >= 'A' && c <= 'F') + { + digit = c - 'A' + 10; + } + + else + return false; + + f.data [j >> 1] *= 16; + f.data [j >> 1] += (uint8) digit; + + } + + fImageUniqueID = f; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ImageUniqueID: "); + + DumpFingerprint (fImageUniqueID); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcCameraOwnerNameExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fOwnerName); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CameraOwnerName: "); + + DumpString (fOwnerName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcCameraSerialNumberExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fCameraSerialNumber); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (fCameraSerialNumber); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcLensSpecificationExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 4)) + return false; + + fLensInfo [0] = stream.TagValue_urational (tagType); + fLensInfo [1] = stream.TagValue_urational (tagType); + fLensInfo [2] = stream.TagValue_urational (tagType); + fLensInfo [3] = stream.TagValue_urational (tagType); + + // Some third party software wrote zero rather than undefined values for + // unknown entries. Work around this bug. + + for (uint32 j = 0; j < 4; j++) + { + + if (fLensInfo [j].IsValid () && fLensInfo [j].As_real64 () <= 0.0) + { + + fLensInfo [j] = dng_urational (0, 0); + + #if qDNGValidate + + ReportWarning ("Zero entry in LensSpecification tag--should be undefined"); + + #endif + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("LensSpecificationExif: "); + + real64 minFL = fLensInfo [0].As_real64 (); + real64 maxFL = fLensInfo [1].As_real64 (); + + if (minFL == maxFL) + printf ("%0.1f mm", minFL); + else + printf ("%0.1f-%0.1f mm", minFL, maxFL); + + if (fLensInfo [2].d) + { + + real64 minFS = fLensInfo [2].As_real64 (); + real64 maxFS = fLensInfo [3].As_real64 (); + + if (minFS == maxFS) + printf (" f/%0.1f", minFS); + else + printf (" f/%0.1f-%0.1f", minFS, maxFS); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcLensMakeExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fLensMake); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (fLensMake); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcLensModelExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fLensName); + + fLensNameWasReadFromExif = fLensName.NotEmpty (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (fLensName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcLensSerialNumberExif: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fLensSerialNumber); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (fLensSerialNumber); + + printf ("\n"); + + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in GPS IFD + +bool dng_exif::Parse_gps (dng_stream &stream, + dng_shared & /* shared */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 /* tagOffset */) + { + + switch (tagCode) + { + + case tcGPSVersionID: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fGPSVersionID = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + printf ("GPSVersionID: %u.%u.%u.%u\n", + (unsigned) b0, + (unsigned) b1, + (unsigned) b2, + (unsigned) b3); + } + + #endif + + break; + + } + + case tcGPSLatitudeRef: + case tcGPSLongitudeRef: + case tcGPSSatellites: + case tcGPSStatus: + case tcGPSMeasureMode: + case tcGPSSpeedRef: + case tcGPSTrackRef: + case tcGPSImgDirectionRef: + case tcGPSMapDatum: + case tcGPSDestLatitudeRef: + case tcGPSDestLongitudeRef: + case tcGPSDestBearingRef: + case tcGPSDestDistanceRef: + case tcGPSDateStamp: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttAscii)) + return false; + + dng_string *s; + + switch (tagCode) + { + + case tcGPSLatitudeRef: + s = &fGPSLatitudeRef; + break; + + case tcGPSLongitudeRef: + s = &fGPSLongitudeRef; + break; + + case tcGPSSatellites: + s = &fGPSSatellites; + break; + + case tcGPSStatus: + s = &fGPSStatus; + break; + + case tcGPSMeasureMode: + s = &fGPSMeasureMode; + break; + + case tcGPSSpeedRef: + s = &fGPSSpeedRef; + break; + + case tcGPSTrackRef: + s = &fGPSTrackRef; + break; + + case tcGPSImgDirectionRef: + s = &fGPSImgDirectionRef; + break; + + case tcGPSMapDatum: + s = &fGPSMapDatum; + break; + + case tcGPSDestLatitudeRef: + s = &fGPSDestLatitudeRef; + break; + + case tcGPSDestLongitudeRef: + s = &fGPSDestLongitudeRef; + break; + + case tcGPSDestBearingRef: + s = &fGPSDestBearingRef; + break; + + case tcGPSDestDistanceRef: + s = &fGPSDestDistanceRef; + break; + + case tcGPSDateStamp: + s = &fGPSDateStamp; + break; + + default: + return false; + + } + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + *s); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (*s); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcGPSLatitude: + case tcGPSLongitude: + case tcGPSTimeStamp: + case tcGPSDestLatitude: + case tcGPSDestLongitude: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttRational)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 3)) + return false; + + dng_urational *u; + + switch (tagCode) + { + + case tcGPSLatitude: + u = fGPSLatitude; + break; + + case tcGPSLongitude: + u = fGPSLongitude; + break; + + case tcGPSTimeStamp: + u = fGPSTimeStamp; + break; + + case tcGPSDestLatitude: + u = fGPSDestLatitude; + break; + + case tcGPSDestLongitude: + u = fGPSDestLongitude; + break; + + default: + return false; + + } + + u [0] = stream.TagValue_urational (tagType); + u [1] = stream.TagValue_urational (tagType); + u [2] = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s:", LookupTagCode (parentCode, tagCode)); + + for (uint32 j = 0; j < 3; j++) + { + + if (u [j].d == 0) + printf (" -"); + + else + printf (" %0.4f", u [j].As_real64 ()); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcGPSAltitudeRef: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fGPSAltitudeRef = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("GPSAltitudeRef: "); + + switch (fGPSAltitudeRef) + { + + case 0: + printf ("Sea level"); + break; + + case 1: + printf ("Sea level reference (negative value)"); + break; + + default: + printf ("%u", (unsigned) fGPSAltitudeRef); + break; + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcGPSAltitude: + case tcGPSDOP: + case tcGPSSpeed: + case tcGPSTrack: + case tcGPSImgDirection: + case tcGPSDestBearing: + case tcGPSDestDistance: + case tcGPSHPositioningError: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttRational)) + return false; + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + dng_urational *u; + + switch (tagCode) + { + + case tcGPSAltitude: + u = &fGPSAltitude; + break; + + case tcGPSDOP: + u = &fGPSDOP; + break; + + case tcGPSSpeed: + u = &fGPSSpeed; + break; + + case tcGPSTrack: + u = &fGPSTrack; + break; + + case tcGPSImgDirection: + u = &fGPSImgDirection; + break; + + case tcGPSDestBearing: + u = &fGPSDestBearing; + break; + + case tcGPSDestDistance: + u = &fGPSDestDistance; + break; + + case tcGPSHPositioningError: + u = &fGPSHPositioningError; + break; + + default: + return false; + + } + + *u = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s:", LookupTagCode (parentCode, tagCode)); + + if (u->d == 0) + printf (" -"); + + else + printf (" %0.4f", u->As_real64 ()); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcGPSProcessingMethod: + case tcGPSAreaInformation: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttUndefined)) + return false; + + dng_string *s; + + switch (tagCode) + { + + case tcGPSProcessingMethod: + s = &fGPSProcessingMethod; + break; + + case tcGPSAreaInformation: + s = &fGPSAreaInformation; + break; + + default: + return false; + + } + + ParseEncodedStringTag (stream, + parentCode, + tagCode, + tagCount, + *s); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: ", LookupTagCode (parentCode, tagCode)); + + DumpString (*s); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcGPSDifferential: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fGPSDifferential = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("GPSDifferential: "); + + switch (fGPSDifferential) + { + + case 0: + printf ("Measurement without differential correction"); + break; + + case 1: + printf ("Differential correction applied"); + break; + + default: + printf ("%u", (unsigned) fGPSDifferential); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in Interoperability IFD + +bool dng_exif::Parse_interoperability (dng_stream &stream, + dng_shared & /* shared */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 /* tagOffset */) + { + + switch (tagCode) + { + + case tcInteroperabilityIndex: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fInteroperabilityIndex); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("InteroperabilityIndex: "); + + DumpString (fInteroperabilityIndex); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcInteroperabilityVersion: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fInteroperabilityVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + + real64 x = (b0 - '0') * 10.00 + + (b1 - '0') * 1.00 + + (b2 - '0') * 0.10 + + (b3 - '0') * 0.01; + + printf ("InteroperabilityVersion: %0.2f\n", x); + + } + + #endif + + break; + + } + + case tcRelatedImageFileFormat: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fRelatedImageFileFormat); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("RelatedImageFileFormat: "); + + DumpString (fRelatedImageFileFormat); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcRelatedImageWidth: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fRelatedImageWidth = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("RelatedImageWidth: %u\n", (unsigned) fRelatedImageWidth); + } + + #endif + + break; + + } + + case tcRelatedImageLength: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fRelatedImageLength = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("RelatedImageLength: %u\n", (unsigned) fRelatedImageLength); + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_exif::PostParse (dng_host & /* host */, + dng_shared & /* shared */) + { + + #if qDNGValidate + + const real64 kAPEX_Slop = 0.25; + + // Sanity check on MaxApertureValue. + + if (fMaxApertureValue.d) + { + + real64 mav = fMaxApertureValue.As_real64 (); + + // Compare against ApertureValue or FNumber. + + real64 av = mav; + + if (fApertureValue.d) + { + + av = fApertureValue.As_real64 (); + + } + + else if (fFNumber.d) + { + + real64 fs = fFNumber.As_real64 (); + + if (fs >= 1.0) + { + + av = FNumberToApertureValue (fs); + + } + + } + + if (mav > av + kAPEX_Slop) + { + + ReportWarning ("MaxApertureValue conflicts with ApertureValue and/or FNumber"); + + } + + // Compare against LensInfo + + if (fLensInfo [2].d && fLensInfo [3].d) + { + + real64 fs1 = fLensInfo [2].As_real64 (); + real64 fs2 = fLensInfo [3].As_real64 (); + + if (fs1 >= 1.0 && fs2 >= 1.0 && fs2 >= fs1) + { + + real64 av1 = FNumberToApertureValue (fs1); + real64 av2 = FNumberToApertureValue (fs2); + + // Wide angle adapters might create an effective + // wide FS, and tele-extenders always result + // in a higher FS. + + if (mav < av1 - kAPEX_Slop - 1.0 || + mav > av2 + kAPEX_Slop + 2.0) + { + + ReportWarning ("Possible MaxApertureValue conflict with LensInfo"); + + } + + } + + } + + } + + // Sanity check on FocalLength. + + if (fFocalLength.d) + { + + real64 fl = fFocalLength.As_real64 (); + + if (fl < 1.0) + { + + ReportWarning ("FocalLength is less than 1.0 mm (legal but unlikely)"); + + } + + else if (fLensInfo [0].d && fLensInfo [1].d) + { + + real64 minFL = fLensInfo [0].As_real64 (); + real64 maxFL = fLensInfo [1].As_real64 (); + + // Allow for wide-angle converters and tele-extenders. + + if (fl < minFL * 0.6 || + fl > maxFL * 2.1) + { + + ReportWarning ("Possible FocalLength conflict with LensInfo"); + + } + + } + + } + + #endif + + // Mirror DateTimeOriginal to DateTime. + + if (fDateTime.NotValid () && fDateTimeOriginal.IsValid ()) + { + + fDateTime = fDateTimeOriginal; + + } + + // Mirror EXIF 2.3 sensitivity tags to ISOSpeedRatings. + + if (fISOSpeedRatings [0] == 0 || fISOSpeedRatings [0] == 65535) + { + + // Prefer Recommended Exposure Index, then Standard Output Sensitivity, then + // ISO Speed, then Exposure Index. + + if (fRecommendedExposureIndex != 0 && + (fSensitivityType == stRecommendedExposureIndex || + fSensitivityType == stSOSandREI || + fSensitivityType == stREIandISOSpeed || + fSensitivityType == stSOSandREIandISOSpeed)) + { + + fISOSpeedRatings [0] = fRecommendedExposureIndex; + + } + + else if (fStandardOutputSensitivity != 0 && + (fSensitivityType == stStandardOutputSensitivity || + fSensitivityType == stSOSandREI || + fSensitivityType == stSOSandISOSpeed || + fSensitivityType == stSOSandREIandISOSpeed)) + { + + fISOSpeedRatings [0] = fStandardOutputSensitivity; + + } + + else if (fISOSpeed != 0 && + (fSensitivityType == stISOSpeed || + fSensitivityType == stSOSandISOSpeed || + fSensitivityType == stREIandISOSpeed || + fSensitivityType == stSOSandREIandISOSpeed)) + { + + fISOSpeedRatings [0] = fISOSpeed; + + } + + } + + // Mirror ExposureIndex to ISOSpeedRatings. + + if (fExposureIndex.IsValid () && fISOSpeedRatings [0] == 0) + { + + fISOSpeedRatings [0] = Round_uint32 (fExposureIndex.As_real64 ()); + + } + + // Kodak sets the GPSAltitudeRef without setting the GPSAltitude. + + if (fGPSAltitude.NotValid ()) + { + + fGPSAltitudeRef = 0xFFFFFFFF; + + } + + // If there is no valid GPS data, clear the GPS version number. + + if (fGPSLatitude [0].NotValid () && + fGPSLongitude [0].NotValid () && + fGPSAltitude .NotValid () && + fGPSTimeStamp [0].NotValid () && + fGPSDateStamp .IsEmpty ()) + { + + fGPSVersionID = 0; + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_exif.h b/source/lib/dng_sdk/dng_exif.h new file mode 100644 index 0000000..a85fc7d --- /dev/null +++ b/source/lib/dng_sdk/dng_exif.h @@ -0,0 +1,351 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_exif.h#2 $ */ +/* $DateTime: 2012/07/11 10:36:56 $ */ +/* $Change: 838485 $ */ +/* $Author: tknoll $ */ + +/** \file + * EXIF read access support. See the \ref spec_exif "EXIF specification" for full + * description of tags. + */ + +/*****************************************************************************/ + +#ifndef __dng_exif__ +#define __dng_exif__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_date_time.h" +#include "dng_fingerprint.h" +#include "dng_types.h" +#include "dng_matrix.h" +#include "dng_rational.h" +#include "dng_string.h" +#include "dng_stream.h" +#include "dng_sdk_limits.h" + +/*****************************************************************************/ + +/// \brief Container class for parsing and holding EXIF tags. +/// +/// Public member fields are documented in \ref spec_exif "EXIF specification." + +class dng_exif + { + + public: + + dng_string fImageDescription; + dng_string fMake; + dng_string fModel; + dng_string fSoftware; + dng_string fArtist; + dng_string fCopyright; + dng_string fCopyright2; + dng_string fUserComment; + + dng_date_time_info fDateTime; + dng_date_time_storage_info fDateTimeStorageInfo; + + dng_date_time_info fDateTimeOriginal; + dng_date_time_storage_info fDateTimeOriginalStorageInfo; + + dng_date_time_info fDateTimeDigitized; + dng_date_time_storage_info fDateTimeDigitizedStorageInfo; + + uint32 fTIFF_EP_StandardID; + uint32 fExifVersion; + uint32 fFlashPixVersion; + + dng_urational fExposureTime; + dng_urational fFNumber; + dng_srational fShutterSpeedValue; + dng_urational fApertureValue; + dng_srational fBrightnessValue; + dng_srational fExposureBiasValue; + dng_urational fMaxApertureValue; + dng_urational fFocalLength; + dng_urational fDigitalZoomRatio; + dng_urational fExposureIndex; + dng_urational fSubjectDistance; + dng_urational fGamma; + + dng_urational fBatteryLevelR; + dng_string fBatteryLevelA; + + uint32 fExposureProgram; + uint32 fMeteringMode; + uint32 fLightSource; + uint32 fFlash; + uint32 fFlashMask; + uint32 fSensingMethod; + uint32 fColorSpace; + uint32 fFileSource; + uint32 fSceneType; + uint32 fCustomRendered; + uint32 fExposureMode; + uint32 fWhiteBalance; + uint32 fSceneCaptureType; + uint32 fGainControl; + uint32 fContrast; + uint32 fSaturation; + uint32 fSharpness; + uint32 fSubjectDistanceRange; + uint32 fSelfTimerMode; + uint32 fImageNumber; + + uint32 fFocalLengthIn35mmFilm; + + uint32 fISOSpeedRatings [3]; // EXIF 2.3: PhotographicSensitivity. + + // Sensitivity tags added in EXIF 2.3. + + uint32 fSensitivityType; + uint32 fStandardOutputSensitivity; + uint32 fRecommendedExposureIndex; + uint32 fISOSpeed; + uint32 fISOSpeedLatitudeyyy; + uint32 fISOSpeedLatitudezzz; + + uint32 fSubjectAreaCount; + uint32 fSubjectArea [4]; + + uint32 fComponentsConfiguration; + + dng_urational fCompresssedBitsPerPixel; + + uint32 fPixelXDimension; + uint32 fPixelYDimension; + + dng_urational fFocalPlaneXResolution; + dng_urational fFocalPlaneYResolution; + + uint32 fFocalPlaneResolutionUnit; + + uint32 fCFARepeatPatternRows; + uint32 fCFARepeatPatternCols; + + uint8 fCFAPattern [kMaxCFAPattern] [kMaxCFAPattern]; + + dng_fingerprint fImageUniqueID; + + uint32 fGPSVersionID; + dng_string fGPSLatitudeRef; + dng_urational fGPSLatitude [3]; + dng_string fGPSLongitudeRef; + dng_urational fGPSLongitude [3]; + uint32 fGPSAltitudeRef; + dng_urational fGPSAltitude; + dng_urational fGPSTimeStamp [3]; + dng_string fGPSSatellites; + dng_string fGPSStatus; + dng_string fGPSMeasureMode; + dng_urational fGPSDOP; + dng_string fGPSSpeedRef; + dng_urational fGPSSpeed; + dng_string fGPSTrackRef; + dng_urational fGPSTrack; + dng_string fGPSImgDirectionRef; + dng_urational fGPSImgDirection; + dng_string fGPSMapDatum; + dng_string fGPSDestLatitudeRef; + dng_urational fGPSDestLatitude [3]; + dng_string fGPSDestLongitudeRef; + dng_urational fGPSDestLongitude [3]; + dng_string fGPSDestBearingRef; + dng_urational fGPSDestBearing; + dng_string fGPSDestDistanceRef; + dng_urational fGPSDestDistance; + dng_string fGPSProcessingMethod; + dng_string fGPSAreaInformation; + dng_string fGPSDateStamp; + uint32 fGPSDifferential; + dng_urational fGPSHPositioningError; + + dng_string fInteroperabilityIndex; + + uint32 fInteroperabilityVersion; + + dng_string fRelatedImageFileFormat; + + uint32 fRelatedImageWidth; + uint32 fRelatedImageLength; + + dng_string fCameraSerialNumber; // EXIF 2.3: BodySerialNumber. + + dng_urational fLensInfo [4]; // EXIF 2.3: LensSpecification. + + dng_string fLensID; + dng_string fLensMake; + dng_string fLensName; // EXIF 2.3: LensModel. + dng_string fLensSerialNumber; + + // Was the lens name field read from a LensModel tag? + + bool fLensNameWasReadFromExif; + + // Private field to hold the approximate focus distance of the lens, in + // meters. This value is often coarsely measured/reported and hence should be + // interpreted only as a rough estimate of the true distance from the plane + // of focus (in object space) to the focal plane. It is still useful for the + // purposes of applying lens corrections. + + dng_urational fApproxFocusDistance; + + dng_srational fFlashCompensation; + + dng_string fOwnerName; // EXIF 2.3: CameraOwnerName. + dng_string fFirmware; + + public: + + dng_exif (); + + virtual ~dng_exif (); + + /// Make clone. + + virtual dng_exif * Clone () const; + + /// Clear all EXIF fields. + + void SetEmpty (); + + /// Copy all GPS-related fields. + /// \param exif Source object from which to copy GPS fields. + + void CopyGPSFrom (const dng_exif &exif); + + /// Utility to fix up common errors and rounding issues with EXIF exposure + /// times. + + static real64 SnapExposureTime (real64 et); + + /// Set exposure time and shutter speed fields. Optionally fix up common + /// errors and rounding issues with EXIF exposure times. + /// \param et Exposure time in seconds. + /// \param snap Set to true to fix up common errors and rounding issues with + /// EXIF exposure times. + + void SetExposureTime (real64 et, + bool snap = true); + + /// Set shutter speed value (APEX units) and exposure time. + /// \param Shutter speed in APEX units. + + void SetShutterSpeedValue (real64 ss); + + /// Utility to encode f-number as a rational. + /// \param fs The f-number to encode. + + static dng_urational EncodeFNumber (real64 fs); + + /// Set the FNumber and ApertureValue fields. + /// \param fs The f-number to set. + + void SetFNumber (real64 fs); + + /// Set the FNumber and ApertureValue fields. + /// \param av The aperture value (APEX units). + + void SetApertureValue (real64 av); + + /// Utility to convert aperture value (APEX units) to f-number. + /// \param av The aperture value (APEX units) to convert. + + static real64 ApertureValueToFNumber (real64 av); + + /// Utility to convert aperture value (APEX units) to f-number. + /// \param av The aperture value (APEX units) to convert. + + static real64 ApertureValueToFNumber (const dng_urational &av); + + /// Utility to convert f-number to aperture value (APEX units). + /// \param fNumber The f-number to convert. + + static real64 FNumberToApertureValue (real64 fNumber); + + /// Utility to convert f-number to aperture value (APEX units). + /// \param fNumber The f-number to convert. + + static real64 FNumberToApertureValue (const dng_urational &fNumber); + + /// Set the DateTime field. + /// \param dt The DateTime value. + + void UpdateDateTime (const dng_date_time_info &dt); + + /// Returns true iff the EXIF version is at least 2.3. + + bool AtLeastVersion0230 () const; + + virtual bool ParseTag (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + bool isMainIFD, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual void PostParse (dng_host &host, + dng_shared &shared); + + protected: + + virtual bool Parse_ifd0 (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual bool Parse_ifd0_main (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual bool Parse_ifd0_exif (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual bool Parse_gps (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual bool Parse_interoperability (dng_stream &stream, + dng_shared &shared, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_fast_module.h b/source/lib/dng_sdk/dng_fast_module.h new file mode 100644 index 0000000..ee6a10f --- /dev/null +++ b/source/lib/dng_sdk/dng_fast_module.h @@ -0,0 +1,31 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_fast_module.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Include file to set optimization to highest level for performance-critical routines. + * Normal files should have otpimization set to normal level to save code size as there is less + * cache pollution this way. + */ + +/*****************************************************************************/ + +// Include this file in modules that contain routines that should be as fast +// as possible, even at the expense of slight code size increases. + +/*****************************************************************************/ + +#ifdef _MSC_VER +#pragma optimize ("t", on) +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_file_stream.cpp b/source/lib/dng_sdk/dng_file_stream.cpp new file mode 100644 index 0000000..4a8a79f --- /dev/null +++ b/source/lib/dng_sdk/dng_file_stream.cpp @@ -0,0 +1,135 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_file_stream.cpp#2 $ */ +/* $DateTime: 2012/06/01 07:28:57 $ */ +/* $Change: 832715 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_file_stream.h" + +#include "dng_exceptions.h" + +/*****************************************************************************/ + +dng_file_stream::dng_file_stream (const char *filename, + bool output, + uint32 bufferSize) + + : dng_stream ((dng_abort_sniffer *) NULL, + bufferSize, + 0) + + , fFile (NULL) + + { + + fFile = fopen (filename, output ? "wb" : "rb"); + + if (!fFile) + { + + #if qDNGValidate + + ReportError ("Unable to open file", + filename); + + ThrowSilentError (); + + #else + + ThrowOpenFile (); + + #endif + + } + + } + +/*****************************************************************************/ + +dng_file_stream::~dng_file_stream () + { + + if (fFile) + { + fclose (fFile); + fFile = NULL; + } + + } + +/*****************************************************************************/ + +uint64 dng_file_stream::DoGetLength () + { + + if (fseek (fFile, 0, SEEK_END) != 0) + { + + ThrowReadFile (); + + } + + return (uint64) ftell (fFile); + + } + +/*****************************************************************************/ + +void dng_file_stream::DoRead (void *data, + uint32 count, + uint64 offset) + { + + if (fseek (fFile, (long) offset, SEEK_SET) != 0) + { + + ThrowReadFile (); + + } + + uint32 bytesRead = (uint32) fread (data, 1, count, fFile); + + if (bytesRead != count) + { + + ThrowReadFile (); + + } + + } + +/*****************************************************************************/ + +void dng_file_stream::DoWrite (const void *data, + uint32 count, + uint64 offset) + { + + if (fseek (fFile, (uint32) offset, SEEK_SET) != 0) + { + + ThrowWriteFile (); + + } + + uint32 bytesWritten = (uint32) fwrite (data, 1, count, fFile); + + if (bytesWritten != count) + { + + ThrowWriteFile (); + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_file_stream.h b/source/lib/dng_sdk/dng_file_stream.h new file mode 100644 index 0000000..89a46b6 --- /dev/null +++ b/source/lib/dng_sdk/dng_file_stream.h @@ -0,0 +1,78 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_file_stream.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Simple, portable, file read/write support. + */ + +/*****************************************************************************/ + +#ifndef __dng_file_stream__ +#define __dng_file_stream__ + +/*****************************************************************************/ + +#include +#include "dng_stream.h" + +/*****************************************************************************/ + +/// \brief A stream to/from a disk file. See dng_stream for read/write interface + +class dng_file_stream: public dng_stream + { + + private: + + FILE *fFile; + + public: + + /// Open a stream on a file. + /// \param filename Pathname in platform synax. + /// \param output Set to true if writing, false otherwise. + /// \param bufferSize size of internal buffer to use. Defaults to 4k. + + dng_file_stream (const char *filename, + bool output = false, + uint32 bufferSize = kDefaultBufferSize); + + virtual ~dng_file_stream (); + + protected: + + virtual uint64 DoGetLength (); + + virtual void DoRead (void *data, + uint32 count, + uint64 offset); + + virtual void DoWrite (const void *data, + uint32 count, + uint64 offset); + + private: + + // Hidden copy constructor and assignment operator. + + dng_file_stream (const dng_file_stream &stream); + + dng_file_stream & operator= (const dng_file_stream &stream); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_filter_task.cpp b/source/lib/dng_sdk/dng_filter_task.cpp new file mode 100644 index 0000000..f8fc560 --- /dev/null +++ b/source/lib/dng_sdk/dng_filter_task.cpp @@ -0,0 +1,167 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_filter_task.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_filter_task.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_image.h" +#include "dng_memory.h" +#include "dng_tag_types.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_filter_task::dng_filter_task (const dng_image &srcImage, + dng_image &dstImage) + + : fSrcImage (srcImage) + , fDstImage (dstImage) + + , fSrcPlane (0 ) + , fSrcPlanes (srcImage.Planes ()) + , fSrcPixelType (srcImage.PixelType ()) + + , fDstPlane (0 ) + , fDstPlanes (dstImage.Planes ()) + , fDstPixelType (dstImage.PixelType ()) + + , fSrcRepeat (1, 1) + + { + + } + +/*****************************************************************************/ + +dng_filter_task::~dng_filter_task () + { + + } + +/*****************************************************************************/ + +void dng_filter_task::Start (uint32 threadCount, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer * /* sniffer */) + { + + dng_point srcTileSize = SrcTileSize (tileSize); + + uint32 srcPixelSize = TagTypeSize (fSrcPixelType); + + uint32 srcBufferSize = srcTileSize.v * + RoundUpForPixelSize (srcTileSize.h, srcPixelSize) * + srcPixelSize * + fSrcPlanes; + + uint32 dstPixelSize = TagTypeSize (fDstPixelType); + + uint32 dstBufferSize = tileSize.v * + RoundUpForPixelSize (tileSize.h, dstPixelSize) * + dstPixelSize * + fDstPlanes; + + for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++) + { + + fSrcBuffer [threadIndex] . Reset (allocator->Allocate (srcBufferSize)); + + fDstBuffer [threadIndex] . Reset (allocator->Allocate (dstBufferSize)); + + // Zero buffers so any pad bytes have defined values. + + DoZeroBytes (fSrcBuffer [threadIndex]->Buffer (), + fSrcBuffer [threadIndex]->LogicalSize ()); + + DoZeroBytes (fDstBuffer [threadIndex]->Buffer (), + fDstBuffer [threadIndex]->LogicalSize ()); + + } + + } + +/*****************************************************************************/ + +void dng_filter_task::Process (uint32 threadIndex, + const dng_rect &area, + dng_abort_sniffer * /* sniffer */) + { + + // Find source area for this destination area. + + dng_rect srcArea = SrcArea (area); + + // Setup srcBuffer. + + dng_pixel_buffer srcBuffer; + + srcBuffer.fArea = srcArea; + + srcBuffer.fPlane = fSrcPlane; + srcBuffer.fPlanes = fSrcPlanes; + + srcBuffer.fPixelType = fSrcPixelType; + srcBuffer.fPixelSize = TagTypeSize (fSrcPixelType); + + srcBuffer.fPlaneStep = RoundUpForPixelSize (srcArea.W (), + srcBuffer.fPixelSize); + + srcBuffer.fRowStep = srcBuffer.fPlaneStep * + srcBuffer.fPlanes; + + srcBuffer.fData = fSrcBuffer [threadIndex]->Buffer (); + + // Setup dstBuffer. + + dng_pixel_buffer dstBuffer; + + dstBuffer.fArea = area; + + dstBuffer.fPlane = fDstPlane; + dstBuffer.fPlanes = fDstPlanes; + + dstBuffer.fPixelType = fDstPixelType; + dstBuffer.fPixelSize = TagTypeSize (fDstPixelType); + + dstBuffer.fPlaneStep = RoundUpForPixelSize (area.W (), + dstBuffer.fPixelSize); + + dstBuffer.fRowStep = dstBuffer.fPlaneStep * + dstBuffer.fPlanes; + + dstBuffer.fData = fDstBuffer [threadIndex]->Buffer (); + + // Get source pixels. + + fSrcImage.Get (srcBuffer, + dng_image::edge_repeat, + fSrcRepeat.v, + fSrcRepeat.h); + + // Process area. + + ProcessArea (threadIndex, + srcBuffer, + dstBuffer); + + // Save result pixels. + + fDstImage.Put (dstBuffer); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_filter_task.h b/source/lib/dng_sdk/dng_filter_task.h new file mode 100644 index 0000000..e5f932d --- /dev/null +++ b/source/lib/dng_sdk/dng_filter_task.h @@ -0,0 +1,157 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_filter_task.h#2 $ */ +/* $DateTime: 2012/07/11 10:36:56 $ */ +/* $Change: 838485 $ */ +/* $Author: tknoll $ */ + +/** \file + * Specialization of dng_area_task for processing an area from one dng_image to an + * area of another. + */ + +/*****************************************************************************/ + +#ifndef __dng_filter_task__ +#define __dng_filter_task__ + +/*****************************************************************************/ + +#include "dng_area_task.h" +#include "dng_auto_ptr.h" +#include "dng_point.h" +#include "dng_rect.h" +#include "dng_sdk_limits.h" + +/*****************************************************************************/ + +/// \brief Represents a task which filters an area of a source dng_image to an area +/// of a destination dng_image. + +class dng_filter_task: public dng_area_task + { + + protected: + + const dng_image &fSrcImage; + + dng_image &fDstImage; + + uint32 fSrcPlane; + uint32 fSrcPlanes; + uint32 fSrcPixelType; + + uint32 fDstPlane; + uint32 fDstPlanes; + uint32 fDstPixelType; + + dng_point fSrcRepeat; + + AutoPtr fSrcBuffer [kMaxMPThreads]; + AutoPtr fDstBuffer [kMaxMPThreads]; + + public: + + /// Construct a filter task given a source and destination images. + /// \param srcImage Image from which source pixels are read. + /// \param dstImage Image to which result pixels are written. + + dng_filter_task (const dng_image &srcImage, + dng_image &dstImage); + + virtual ~dng_filter_task (); + + /// Compute the source area needed for a given destination area. Default + /// implementation assumes destination area is equal to source area for all + /// cases. + /// + /// \param dstArea Area to for which pixels will be computed. + /// + /// \retval The source area needed as input to calculate the requested + /// destination area. + + virtual dng_rect SrcArea (const dng_rect &dstArea) + { + return dstArea; + } + + /// Given a destination tile size, calculate input tile size. Simlar to + /// SrcArea, and should seldom be overridden. + /// + /// \param dstTileSize The destination tile size that is targeted for output. + /// + /// \retval The source tile size needed to compute a tile of the destination + /// size. + + virtual dng_point SrcTileSize (const dng_point &dstTileSize) + { + return SrcArea (dng_rect (dstTileSize)).Size (); + } + + /// Implements filtering operation from one buffer to another. Source and + /// destination pixels are set up in member fields of this class. Ideally, no + /// allocation should be done in this routine. + /// + /// \param threadIndex The thread on which this routine is being called, + /// between 0 and threadCount - 1 for the threadCount passed to Start method. + /// + /// \param srcBuffer Input area and source pixels. + /// + /// \param dstBuffer Output area and destination pixels. + + virtual void ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) = 0; + + /// Called prior to processing on specific threads. Can be used to allocate + /// per-thread memory buffers, etc. + /// + /// \param threadCount Total number of threads that will be used for + /// processing. Less than or equal to MaxThreads of dng_area_task. + /// + /// \param tileSize Size of source tiles which will be processed. (Not all + /// tiles will be this size due to edge conditions.) + /// + /// \param allocator dng_memory_allocator to use for allocating temporary + /// buffers, etc. + /// + /// \param sniffer Sniffer to test for user cancellation and to set up + /// progress. + + virtual void Start (uint32 threadCount, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer); + + /// Process one tile or partitioned area. Should not be overridden. Instead, + /// override ProcessArea, which is where to implement filter processing for a + /// specific type of dng_filter_task. There is no allocator parameter as all + /// allocation should be done in Start. + /// + /// \param threadIndex 0 to threadCount - 1 index indicating which thread + /// this is. (Can be used to get a thread-specific buffer allocated in the + /// Start method.) + /// + /// \param area Size of tiles to be used for sizing buffers, etc. (Edges of + /// processing can be smaller.) + /// + /// \param sniffer dng_abort_sniffer to use to check for user cancellation + /// and progress updates. + + virtual void Process (uint32 threadIndex, + const dng_rect &area, + dng_abort_sniffer *sniffer); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_fingerprint.cpp b/source/lib/dng_sdk/dng_fingerprint.cpp new file mode 100644 index 0000000..5256873 --- /dev/null +++ b/source/lib/dng_sdk/dng_fingerprint.cpp @@ -0,0 +1,589 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_fingerprint.cpp#3 $ */ +/* $DateTime: 2012/07/11 10:36:56 $ */ +/* $Change: 838485 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_fingerprint.h" + +#include "dng_assertions.h" +#include "dng_flags.h" + +/*****************************************************************************/ + +dng_fingerprint::dng_fingerprint () + { + + for (uint32 j = 0; j < 16; j++) + { + + data [j] = 0; + + } + + } + +/*****************************************************************************/ + +bool dng_fingerprint::IsNull () const + { + + for (uint32 j = 0; j < 16; j++) + { + + if (data [j] != 0) + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_fingerprint::operator== (const dng_fingerprint &print) const + { + + for (uint32 j = 0; j < 16; j++) + { + + if (data [j] != print.data [j]) + { + + return false; + + } + + } + + return true; + + } + +/******************************************************************************/ + +uint32 dng_fingerprint::Collapse32 () const + { + + uint32 x = 0; + + for (uint32 j = 0; j < 4; j++) + { + + uint32 y = 0; + + for (uint32 k = 0; k < 4; k++) + { + + y = (y << 8) + (uint32) data [j * 4 + k]; + + } + + x = x ^ y; + + } + + return x; + + } + +/******************************************************************************/ + +static char NumToHexChar (unsigned int c) + { + + if (c < 10) + { + return (char) ('0' + c); + } + + else + { + return (char) ('A' + c - 10); + } + + } + +/*****************************************************************************/ + +void dng_fingerprint::ToUtf8HexString (char resultStr [2 * kDNGFingerprintSize + 1]) const + { + + for (size_t i = 0; i < kDNGFingerprintSize; i++) + { + + unsigned char c = data [i]; + + resultStr [i * 2] = NumToHexChar (c >> 4); + resultStr [i * 2 + 1] = NumToHexChar (c & 15); + + } + + resultStr [kDNGFingerprintSize * 2] = '\0'; + + } + +/******************************************************************************/ + +static int HexCharToNum (char hexChar) + { + + if (hexChar >= '0' && hexChar <= '9') + { + return hexChar - '0'; + } + + else if (hexChar >= 'A' && hexChar <= 'F') + { + return hexChar - 'A' + 10; + } + + else if (hexChar >= 'a' && hexChar <= 'f') + { + return hexChar - 'a' + 10; + } + + return -1; + + } + +/*****************************************************************************/ + +bool dng_fingerprint::FromUtf8HexString (const char inputStr [2 * kDNGFingerprintSize + 1]) + { + + for (size_t i = 0; i < kDNGFingerprintSize; i++) + { + + int highNibble = HexCharToNum (inputStr [i * 2]); + + if (highNibble < 0) + { + return false; + } + + int lowNibble = HexCharToNum (inputStr [i * 2 + 1]); + + if (lowNibble < 0) + { + return false; + } + + data [i] = (uint8) ((highNibble << 4) + lowNibble); + + } + + return true; + + } + +/******************************************************************************/ + +// Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm + +// Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +// rights reserved. +// +// License to copy and use this software is granted provided that it +// is identified as the "RSA Data Security, Inc. MD5 Message-Digest +// Algorithm" in all material mentioning or referencing this software +// or this function. +// +// License is also granted to make and use derivative works provided +// that such works are identified as "derived from the RSA Data +// Security, Inc. MD5 Message-Digest Algorithm" in all material +// mentioning or referencing the derived work. +// +// RSA Data Security, Inc. makes no representations concerning either +// the merchantability of this software or the suitability of this +// software for any particular purpose. It is provided "as is" +// without express or implied warranty of any kind. +// +// These notices must be retained in any copies of any part of this +// documentation and/or software. + +/******************************************************************************/ + +dng_md5_printer::dng_md5_printer () + + : final (false) + , result () + + { + + Reset (); + + } + +/******************************************************************************/ + +void dng_md5_printer::Reset () + { + + // No bits processed yet. + + count [0] = 0; + count [1] = 0; + + // Load magic initialization constants. + + state [0] = 0x67452301; + state [1] = 0xefcdab89; + state [2] = 0x98badcfe; + state [3] = 0x10325476; + + // Not finalized yet. + + final = false; + + } + +/******************************************************************************/ + +void dng_md5_printer::Process (const void *data, + uint32 inputLen) + { + + DNG_ASSERT (!final, "Fingerprint already finalized!"); + + const uint8 *input = (const uint8 *) data; + + // Compute number of bytes mod 64 + + uint32 index = (count [0] >> 3) & 0x3F; + + // Update number of bits + + if ((count [0] += inputLen << 3) < (inputLen << 3)) + { + count [1]++; + } + + count [1] += inputLen >> 29; + + // Transform as many times as possible. + + uint32 i = 0; + + uint32 partLen = 64 - index; + + if (inputLen >= partLen) + { + + memcpy (&buffer [index], + input, + partLen); + + MD5Transform (state, buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + { + + MD5Transform (state, &input [i]); + + } + + index = 0; + + } + + // Buffer remaining input + + memcpy (&buffer [index], + &input [i], + inputLen - i); + + } + +/******************************************************************************/ + +const dng_fingerprint & dng_md5_printer::Result () + { + + if (!final) + { + + static uint8 PADDING [64] = + { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 + }; + + // Save number of bits + + uint8 bits [8]; + + Encode (bits, count, 8); + + // Pad out to 56 mod 64. + + uint32 index = (count [0] >> 3) & 0x3f; + + uint32 padLen = (index < 56) ? (56 - index) : (120 - index); + + Process (PADDING, padLen); + + // Append length (before padding) + + Process (bits, 8); + + // Store state in digest + + Encode (result.data, state, 16); + + // We are now finalized. + + final = true; + + } + + return result; + + } + +/******************************************************************************/ + +// Encodes input (uint32) into output (uint8). Assumes len is +// a multiple of 4. + +void dng_md5_printer::Encode (uint8 *output, + const uint32 *input, + uint32 len) + { + + uint32 i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + output [j ] = (uint8) ((input [i] ) & 0xff); + output [j+1] = (uint8) ((input [i] >> 8) & 0xff); + output [j+2] = (uint8) ((input [i] >> 16) & 0xff); + output [j+3] = (uint8) ((input [i] >> 24) & 0xff); + } + + } + +/******************************************************************************/ + +// Decodes input (uint8) into output (uint32). Assumes len is +// a multiple of 4. + +void dng_md5_printer::Decode (uint32 *output, + const uint8 *input, + uint32 len) + { + + // Check for non-aligned case. + + if (((uintptr) input) & 3) + { + + uint32 i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + { + + output [i] = (((uint32) input [j ]) ) | + (((uint32) input [j+1]) << 8) | + (((uint32) input [j+2]) << 16) | + (((uint32) input [j+3]) << 24); + + } + + } + + // Else use optimized code for aligned case. + + else + { + + len = len >> 2; + + const uint32 *sPtr = (const uint32 *) input; + + uint32 *dPtr = output; + + while (len--) + { + + #if qDNGBigEndian + + uint32 data = *(sPtr++); + + data = (data >> 24) | + ((data >> 8) & 0x0000FF00) | + ((data << 8) & 0x00FF0000) | + (data << 24); + + *(dPtr++) = data; + + #else + + *(dPtr++) = *(sPtr++); + + #endif + + } + + } + + } + +/******************************************************************************/ + +// MD5 basic transformation. Transforms state based on block. + +void dng_md5_printer::MD5Transform (uint32 state [4], + const uint8 block [64]) + { + + enum + { + S11 = 7, + S12 = 12, + S13 = 17, + S14 = 22, + S21 = 5, + S22 = 9, + S23 = 14, + S24 = 20, + S31 = 4, + S32 = 11, + S33 = 16, + S34 = 23, + S41 = 6, + S42 = 10, + S43 = 15, + S44 = 21 + }; + + #if qDNGBigEndian + + uint32 x [16]; + + Decode (x, block, 64); + + #else + + uint32 temp [16]; + + const uint32 *x; + + if (((uintptr) block) & 3) + { + + Decode (temp, block, 64); + + x = temp; + + } + + else + x = (const uint32 *) block; + + #endif + + uint32 a = state [0]; + uint32 b = state [1]; + uint32 c = state [2]; + uint32 d = state [3]; + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state [0] += a; + state [1] += b; + state [2] += c; + state [3] += d; + + } + +/*****************************************************************************/ + +// End of RSA Data Security, Inc. derived code. + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_fingerprint.h b/source/lib/dng_sdk/dng_fingerprint.h new file mode 100644 index 0000000..b8c5cfb --- /dev/null +++ b/source/lib/dng_sdk/dng_fingerprint.h @@ -0,0 +1,377 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_fingerprint.h#2 $ */ +/* $DateTime: 2012/07/11 10:36:56 $ */ +/* $Change: 838485 $ */ +/* $Author: tknoll $ */ + +/** \file + * Fingerprint (cryptographic hashing) support for generating strong hashes of image + * data. + */ + +/*****************************************************************************/ + +#ifndef __dng_fingerprint__ +#define __dng_fingerprint__ + +/*****************************************************************************/ + +#include "dng_exceptions.h" +#include "dng_types.h" +#include "dng_stream.h" + +#include + +/*****************************************************************************/ + +/// \brief Container fingerprint (MD5 only at present). + +class dng_fingerprint + { + + public: + + static const size_t kDNGFingerprintSize = 16; + + uint8 data [kDNGFingerprintSize]; + + public: + + dng_fingerprint (); + + /// Check if fingerprint is all zeros. + + bool IsNull () const; + + /// Same as IsNull but expresses intention of testing validity. + + bool IsValid () const + { + return !IsNull (); + } + + /// Set to all zeros, a value used to indicate an invalid fingerprint. + + void Clear () + { + *this = dng_fingerprint (); + } + + /// Test if two fingerprints are equal. + + bool operator== (const dng_fingerprint &print) const; + + /// Test if two fingerprints are not equal. + + bool operator!= (const dng_fingerprint &print) const + { + return !(*this == print); + } + + /// Produce a 32-bit hash value from fingerprint used for faster hashing of + /// fingerprints. + + uint32 Collapse32 () const; + + /// Convert fingerprint to UTF-8 string. + /// + /// \param resultStr The output array to which the UTF-8 encoding of the + /// fingerprint will be written. + + void ToUtf8HexString (char resultStr [2 * kDNGFingerprintSize + 1]) const; + + /// Convert UTF-8 string to fingerprint. Returns true on success, false on + /// failure. + /// + /// \param inputStr The input array from which the UTF-8 encoding of the + /// fingerprint will be read. + /// + /// \retval True indicates success. + + bool FromUtf8HexString (const char inputStr [2 * kDNGFingerprintSize + 1]); + + }; + +/*****************************************************************************/ + +/// \brief Utility to compare fingerprints (e.g., for sorting). + +struct dng_fingerprint_less_than + { + + /// Less-than comparison. + + bool operator() (const dng_fingerprint &a, + const dng_fingerprint &b) const + { + + return memcmp (a.data, + b.data, + sizeof (a.data)) < 0; + + } + + }; + +/******************************************************************************/ + +// Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm. + +// Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All +// rights reserved. +// +// License to copy and use this software is granted provided that it +// is identified as the "RSA Data Security, Inc. MD5 Message-Digest +// Algorithm" in all material mentioning or referencing this software +// or this function. +// +// License is also granted to make and use derivative works provided +// that such works are identified as "derived from the RSA Data +// Security, Inc. MD5 Message-Digest Algorithm" in all material +// mentioning or referencing the derived work. +// +// RSA Data Security, Inc. makes no representations concerning either +// the merchantability of this software or the suitability of this +// software for any particular purpose. It is provided "as is" +// without express or implied warranty of any kind. +// +// These notices must be retained in any copies of any part of this +// documentation and/or software. + +/// \brief Class to hash binary data to a fingerprint using the MD5 Message-Digest +/// Algorithm. + +class dng_md5_printer + { + + public: + + dng_md5_printer (); + + virtual ~dng_md5_printer () + { + } + + /// Reset the fingerprint. + + void Reset (); + + /// Append the data to the stream to be hashed. + /// \param data The data to be hashed. + /// \param inputLen The length of data, in bytes. + + void Process (const void *data, + uint32 inputLen); + + /// Append the string to the stream to be hashed. + /// \param text The string to be hashed. + + void Process (const char *text) + { + + Process (text, (uint32) strlen (text)); + + } + + /// Get the fingerprint (i.e., result of the hash). + + const dng_fingerprint & Result (); + + private: + + static void Encode (uint8 *output, + const uint32 *input, + uint32 len); + + static void Decode (uint32 *output, + const uint8 *input, + uint32 len); + + // F, G, H and I are basic MD5 functions. + + static inline uint32 F (uint32 x, + uint32 y, + uint32 z) + { + return (x & y) | (~x & z); + } + + static inline uint32 G (uint32 x, + uint32 y, + uint32 z) + { + return (x & z) | (y & ~z); + } + + static inline uint32 H (uint32 x, + uint32 y, + uint32 z) + { + return x ^ y ^ z; + } + + static inline uint32 I (uint32 x, + uint32 y, + uint32 z) + { + return y ^ (x | ~z); + } + + // FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. + + static inline void FF (uint32 &a, + uint32 b, + uint32 c, + uint32 d, + uint32 x, + uint32 s, + uint32 ac) + { + a += F (b, c, d) + x + ac; + a = (a << s) | (a >> (32 - s)); + a += b; + } + + static inline void GG (uint32 &a, + uint32 b, + uint32 c, + uint32 d, + uint32 x, + uint32 s, + uint32 ac) + { + a += G (b, c, d) + x + ac; + a = (a << s) | (a >> (32 - s)); + a += b; + } + + static inline void HH (uint32 &a, + uint32 b, + uint32 c, + uint32 d, + uint32 x, + uint32 s, + uint32 ac) + { + a += H (b, c, d) + x + ac; + a = (a << s) | (a >> (32 - s)); + a += b; + } + + static inline void II (uint32 &a, + uint32 b, + uint32 c, + uint32 d, + uint32 x, + uint32 s, + uint32 ac) + { + a += I (b, c, d) + x + ac; + a = (a << s) | (a >> (32 - s)); + a += b; + } + + static void MD5Transform (uint32 state [4], + const uint8 block [64]); + + private: + + uint32 state [4]; + + uint32 count [2]; + + uint8 buffer [64]; + + bool final; + + dng_fingerprint result; + + }; + +/*****************************************************************************/ + +/// \brief A dng_stream based interface to the MD5 printing logic. + +class dng_md5_printer_stream : public dng_stream, dng_md5_printer + { + + private: + + uint64 fNextOffset; + + public: + + /// Create an empty MD5 printer stream. + + dng_md5_printer_stream () + + : fNextOffset (0) + + { + } + + virtual uint64 DoGetLength () + { + + return fNextOffset; + + } + + virtual void DoRead (void * /* data */, + uint32 /* count */, + uint64 /* offset */) + { + + ThrowProgramError (); + + } + + virtual void DoSetLength (uint64 length) + { + + if (length != fNextOffset) + { + ThrowProgramError (); + } + + } + + virtual void DoWrite (const void *data, + uint32 count2, + uint64 offset) + { + + if (offset != fNextOffset) + { + ThrowProgramError (); + } + + Process (data, count2); + + fNextOffset += count2; + + } + + const dng_fingerprint & Result () + { + + Flush (); + + return dng_md5_printer::Result (); + + } + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_flags.h b/source/lib/dng_sdk/dng_flags.h new file mode 100644 index 0000000..b0ab558 --- /dev/null +++ b/source/lib/dng_sdk/dng_flags.h @@ -0,0 +1,267 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_flags.h#5 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * Conditional compilation flags for DNG SDK. + * + * All conditional compilation macros for the DNG SDK begin with a lowercase 'q'. + */ + +/*****************************************************************************/ + +#ifndef __dng_flags__ +#define __dng_flags__ + +#define METADATA_CLEANUP 0 + +#include "gpr_platform.h" + +/*****************************************************************************/ + +/// \def qMacOS +/// 1 if compiling for Mac OS X. + +/// \def qWinOS +/// 1 if compiling for Windows. + +// Make sure qMacOS and qWinOS are defined. + +#if !defined(qMacOS) && !defined(qWinOS) +#include "RawEnvironment.h" +#endif + +#if !defined(qMacOS) && !defined(qWinOS) +#error Unable to figure out platform +#endif + + +/*****************************************************************************/ + +// Platforms. + +#ifndef qImagecore +#define qImagecore 0 +#endif + +#ifndef qiPhone +#define qiPhone 0 +#endif + +#ifndef qiPhoneSimulator +#define qiPhoneSimulator 0 +#endif + +#ifndef qAndroid +#define qAndroid 0 +#endif + +#ifndef qAndroidArm7 +#define qAndroidArm7 0 +#endif + +/*****************************************************************************/ + +// Establish WIN32 and WIN64 definitions. + +#if defined(_WIN32) && !defined(WIN32) +#define WIN32 1 +#endif + +#if defined(_WIN64) && !defined(WIN64) +#define WIN64 1 +#endif + +/*****************************************************************************/ + +/// \def qDNGDebug +/// 1 if debug code is compiled in, 0 otherwise. Enables assertions and other debug +/// checks in exchange for slower processing. + +// Figure out if debug build or not. + +#ifndef qDNGDebug + +#if defined(Debug) +#define qDNGDebug Debug + +#elif defined(_DEBUG) +#define qDNGDebug _DEBUG + +#else +#define qDNGDebug 0 + +#endif +#endif + +/*****************************************************************************/ + +// Figure out byte order. + +/// \def qDNGBigEndian +/// 1 if this target platform is big endian (e.g. PowerPC Macintosh), else 0. +/// +/// \def qDNGLittleEndian +/// 1 if this target platform is little endian (e.g. x86 processors), else 0. + +#ifndef qDNGBigEndian + +#if defined(qDNGLittleEndian) +#define qDNGBigEndian !qDNGLittleEndian + +#elif defined(__POWERPC__) +#define qDNGBigEndian 1 + +#elif defined(__INTEL__) +#define qDNGBigEndian 0 + +#elif defined(_M_IX86) +#define qDNGBigEndian 0 + +#elif defined(_M_X64) || defined(__amd64__) +#define qDNGBigEndian 0 + +#elif defined(__LITTLE_ENDIAN__) +#define qDNGBigEndian 0 + +#elif defined(__BIG_ENDIAN__) +#define qDNGBigEndian 1 + +#elif defined(_ARM_) +#define qDNGBigEndian 0 + +#else + +#ifndef qXCodeRez +#error Unable to figure out byte order. +#endif + +#endif +#endif + +#ifndef qXCodeRez + +#ifndef qDNGLittleEndian +#define qDNGLittleEndian !qDNGBigEndian +#endif + +#endif + +/*****************************************************************************/ + +/// \def qDNG64Bit +/// 1 if this target platform uses 64-bit addresses, 0 otherwise. + +#ifndef qDNG64Bit + +#if qMacOS + +#ifdef __LP64__ +#if __LP64__ +#define qDNG64Bit 1 +#endif +#endif + +#elif qWinOS + +#ifdef WIN64 +#if WIN64 +#define qDNG64Bit 1 +#endif +#endif + +#endif + +#ifndef qDNG64Bit +#define qDNG64Bit 0 +#endif + +#endif + +/*****************************************************************************/ + +/// \def qDNGThreadSafe +/// 1 if target platform has thread support and threadsafe libraries, 0 otherwise. + +#ifndef qDNGThreadSafe +#define qDNGThreadSafe (qMacOS || qWinOS) +#endif + +/*****************************************************************************/ + +/// \def qDNGValidateTarget +/// 1 if dng_validate command line tool is being built, 0 otherwise. + +#ifndef qDNGValidateTarget +#define qDNGValidateTarget 0 +#endif + +/*****************************************************************************/ + +/// \def qDNGValidate +/// 1 if DNG validation code is enabled, 0 otherwise. + +#ifndef qDNGValidate +#define qDNGValidate qDNGValidateTarget +#endif + +/*****************************************************************************/ + +/// \def qDNGPrintMessages +/// 1 if dng_show_message should use fprintf to stderr. 0 if it should use a platform +/// specific interrupt mechanism. + +#ifndef qDNGPrintMessages +#define qDNGPrintMessages qDNGValidate +#endif + +/*****************************************************************************/ + +/// \def qDNGCodec +/// 1 to build the Windows Imaging Component Codec (e.g. for Vista). + +#ifndef qDNGCodec +#define qDNGCodec 0 +#endif + +/*****************************************************************************/ + +// Experimental features -- work in progress for Lightroom 4.0 and Camera Raw 7.0. +// Turn this off for Lightroom 3.x & Camera Raw 6.x dot releases. + +#ifndef qDNGExperimental +#define qDNGExperimental 1 +#endif + +/*****************************************************************************/ + +/// \def qDNGXMPFiles +/// 1 to use XMPFiles. + +#ifndef qDNGXMPFiles +#define qDNGXMPFiles 0 +#endif + +/*****************************************************************************/ + +/// \def qDNGXMPDocOps +/// 1 to use XMPDocOps. + +#ifndef qDNGXMPDocOps +#define qDNGXMPDocOps 0 +#endif + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_gain_map.cpp b/source/lib/dng_sdk/dng_gain_map.cpp new file mode 100644 index 0000000..48114eb --- /dev/null +++ b/source/lib/dng_sdk/dng_gain_map.cpp @@ -0,0 +1,582 @@ +/*****************************************************************************/ +// Copyright 2008-2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_gain_map.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_gain_map.h" + +#include "dng_exceptions.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_pixel_buffer.h" +#include "dng_stream.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +class dng_gain_map_interpolator + { + + private: + + const dng_gain_map &fMap; + + dng_point_real64 fScale; + dng_point_real64 fOffset; + + int32 fColumn; + int32 fPlane; + + uint32 fRowIndex1; + uint32 fRowIndex2; + real32 fRowFract; + + int32 fResetColumn; + + real32 fValueBase; + real32 fValueStep; + real32 fValueIndex; + + public: + + dng_gain_map_interpolator (const dng_gain_map &map, + const dng_rect &mapBounds, + int32 row, + int32 column, + uint32 plane); + + real32 Interpolate () const + { + + return fValueBase + fValueStep * fValueIndex; + + } + + void Increment () + { + + if (++fColumn >= fResetColumn) + { + + ResetColumn (); + + } + + else + { + + fValueIndex += 1.0f; + + } + + } + + private: + + real32 InterpolateEntry (uint32 colIndex); + + void ResetColumn (); + + }; + +/*****************************************************************************/ + +dng_gain_map_interpolator::dng_gain_map_interpolator (const dng_gain_map &map, + const dng_rect &mapBounds, + int32 row, + int32 column, + uint32 plane) + + : fMap (map) + + , fScale (1.0 / mapBounds.H (), + 1.0 / mapBounds.W ()) + + , fOffset (0.5 - mapBounds.t, + 0.5 - mapBounds.l) + + , fColumn (column) + , fPlane (plane) + + , fRowIndex1 (0) + , fRowIndex2 (0) + , fRowFract (0.0f) + + , fResetColumn (0) + + , fValueBase (0.0f) + , fValueStep (0.0f) + , fValueIndex (0.0f) + + { + + real64 rowIndexF = (fScale.v * (row + fOffset.v) - + fMap.Origin ().v) / fMap.Spacing ().v; + + if (rowIndexF <= 0.0) + { + + fRowIndex1 = 0; + fRowIndex2 = 0; + + fRowFract = 0.0f; + + } + + else + { + + fRowIndex1 = (uint32) rowIndexF; + + if ((int32) fRowIndex1 >= fMap.Points ().v - 1) + { + + fRowIndex1 = fMap.Points ().v - 1; + fRowIndex2 = fRowIndex1; + + fRowFract = 0.0f; + + } + + else + { + + fRowIndex2 = fRowIndex1 + 1; + + fRowFract = (real32) (rowIndexF - (real64) fRowIndex1); + + } + + } + + ResetColumn (); + + } + +/*****************************************************************************/ + +real32 dng_gain_map_interpolator::InterpolateEntry (uint32 colIndex) + { + + return fMap.Entry (fRowIndex1, colIndex, fPlane) * (1.0f - fRowFract) + + fMap.Entry (fRowIndex2, colIndex, fPlane) * ( fRowFract); + + } + +/*****************************************************************************/ + +void dng_gain_map_interpolator::ResetColumn () + { + + real64 colIndexF = ((fScale.h * (fColumn + fOffset.h)) - + fMap.Origin ().h) / fMap.Spacing ().h; + + if (colIndexF <= 0.0) + { + + fValueBase = InterpolateEntry (0); + + fValueStep = 0.0f; + + fResetColumn = (int32) ceil (fMap.Origin ().h / fScale.h - fOffset.h); + + } + + else + { + + uint32 colIndex = (uint32) colIndexF; + + if ((int32) colIndex >= fMap.Points ().h - 1) + { + + fValueBase = InterpolateEntry (fMap.Points ().h - 1); + + fValueStep = 0.0f; + + fResetColumn = 0x7FFFFFFF; + + } + + else + { + + real64 base = InterpolateEntry (colIndex); + real64 delta = InterpolateEntry (colIndex + 1) - base; + + fValueBase = (real32) (base + delta * (colIndexF - (real64) colIndex)); + + fValueStep = (real32) ((delta * fScale.h) / fMap.Spacing ().h); + + fResetColumn = (int32) ceil (((colIndex + 1) * fMap.Spacing ().h + + fMap.Origin ().h) / fScale.h - fOffset.h); + + } + + } + + fValueIndex = 0.0f; + + } + +/*****************************************************************************/ + +dng_gain_map::dng_gain_map (dng_memory_allocator &allocator, + const dng_point &points, + const dng_point_real64 &spacing, + const dng_point_real64 &origin, + uint32 planes) + + : fPoints (points) + , fSpacing (spacing) + , fOrigin (origin) + , fPlanes (planes) + + , fRowStep (planes * points.h) + + , fBuffer () + + { + + fBuffer.Reset (allocator.Allocate (fPoints.v * + fPoints.h * + fPlanes * (uint32) sizeof (real32))); + + } + +/*****************************************************************************/ + +real32 dng_gain_map::Interpolate (int32 row, + int32 col, + uint32 plane, + const dng_rect &bounds) const + { + + dng_gain_map_interpolator interp (*this, + bounds, + row, + col, + plane); + + return interp.Interpolate (); + + } + +/*****************************************************************************/ + +uint32 dng_gain_map::PutStreamSize () const + { + + return 44 + fPoints.v * fPoints.h * fPlanes * 4; + + } + +/*****************************************************************************/ + +void dng_gain_map::PutStream (dng_stream &stream) const + { + + stream.Put_uint32 (fPoints.v); + stream.Put_uint32 (fPoints.h); + + stream.Put_real64 (fSpacing.v); + stream.Put_real64 (fSpacing.h); + + stream.Put_real64 (fOrigin.v); + stream.Put_real64 (fOrigin.h); + + stream.Put_uint32 (fPlanes); + + for (int32 rowIndex = 0; rowIndex < fPoints.v; rowIndex++) + { + + for (int32 colIndex = 0; colIndex < fPoints.h; colIndex++) + { + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + stream.Put_real32 (Entry (rowIndex, + colIndex, + plane)); + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_gain_map * dng_gain_map::GetStream (dng_stream &stream, dng_memory_allocator &allocator) + { + + dng_point mapPoints; + + mapPoints.v = stream.Get_uint32 (); + mapPoints.h = stream.Get_uint32 (); + + dng_point_real64 mapSpacing; + + mapSpacing.v = stream.Get_real64 (); + mapSpacing.h = stream.Get_real64 (); + + dng_point_real64 mapOrigin; + + mapOrigin.v = stream.Get_real64 (); + mapOrigin.h = stream.Get_real64 (); + + uint32 mapPlanes = stream.Get_uint32 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Points: v=%d, h=%d\n", + (int) mapPoints.v, + (int) mapPoints.h); + + printf ("Spacing: v=%.6f, h=%.6f\n", + mapSpacing.v, + mapSpacing.h); + + printf ("Origin: v=%.6f, h=%.6f\n", + mapOrigin.v, + mapOrigin.h); + + printf ("Planes: %u\n", + (unsigned) mapPlanes); + + } + + #endif + + if (mapPoints.v == 1) + { + mapSpacing.v = 1.0; + mapOrigin.v = 0.0; + } + + if (mapPoints.h == 1) + { + mapSpacing.h = 1.0; + mapOrigin.h = 0.0; + } + + if (mapPoints.v < 1 || + mapPoints.h < 1 || + mapSpacing.v <= 0.0 || + mapSpacing.h <= 0.0 || + mapPlanes < 1) + { + ThrowBadFormat (); + } + + AutoPtr map (new dng_gain_map (allocator, + mapPoints, + mapSpacing, + mapOrigin, + mapPlanes)); + + #if qDNGValidate + + uint32 linesPrinted = 0; + uint32 linesSkipped = 0; + + #endif + + for (int32 rowIndex = 0; rowIndex < mapPoints.v; rowIndex++) + { + + for (int32 colIndex = 0; colIndex < mapPoints.h; colIndex++) + { + + for (uint32 plane = 0; plane < mapPlanes; plane++) + { + + real32 x = stream.Get_real32 (); + + map->Entry (rowIndex, colIndex, plane) = x; + + #if qDNGValidate + + if (gVerbose) + { + + if (linesPrinted < gDumpLineLimit) + { + + printf (" Map [%3u] [%3u] [%u] = %.4f\n", + (unsigned) rowIndex, + (unsigned) colIndex, + (unsigned) plane, + x); + + linesPrinted++; + + } + + else + linesSkipped++; + + } + + #endif + + } + + } + + } + + #if qDNGValidate + + if (linesSkipped) + { + + printf (" ... %u map entries skipped\n", (unsigned) linesSkipped); + + } + + #endif + + return map.Release (); + + } + +/*****************************************************************************/ + +dng_opcode_GainMap::dng_opcode_GainMap (const dng_area_spec &areaSpec, + AutoPtr &gainMap) + + : dng_inplace_opcode (dngOpcode_GainMap, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + + , fGainMap () + + { + + fGainMap.Reset (gainMap.Release ()); + + } + +/*****************************************************************************/ + +dng_opcode_GainMap::dng_opcode_GainMap (dng_host &host, + dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_GainMap, + stream, + "GainMap") + + , fAreaSpec () + + , fGainMap () + + { + + uint32 byteCount = stream.Get_uint32 (); + + uint64 startPosition = stream.Position (); + + fAreaSpec.GetData (stream); + + fGainMap.Reset (dng_gain_map::GetStream (stream, host.Allocator())); + + if (stream.Position () != startPosition + byteCount) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +void dng_opcode_GainMap::PutData (dng_stream &stream) const + { + + stream.Put_uint32 (dng_area_spec::kDataSize + + fGainMap->PutStreamSize ()); + + fAreaSpec.PutData (stream); + + fGainMap->PutStream (stream); + + } + +/*****************************************************************************/ + +void dng_opcode_GainMap::ProcessArea (dng_negative & /* negative */, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + uint32 cols = overlap.W (); + + uint32 colPitch = fAreaSpec.ColPitch (); + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + uint32 mapPlane = Min_uint32 (plane, fGainMap->Planes () - 1); + + for (int32 row = overlap.t; row < overlap.b; row += fAreaSpec.RowPitch ()) + { + + real32 *dPtr = buffer.DirtyPixel_real32 (row, overlap.l, plane); + + dng_gain_map_interpolator interp (*fGainMap, + imageBounds, + row, + overlap.l, + mapPlane); + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 gain = interp.Interpolate (); + + dPtr [col] = Min_real32 (dPtr [col] * gain, 1.0f); + + for (uint32 j = 0; j < colPitch; j++) + { + interp.Increment (); + } + + } + + } + + } + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_gain_map.h b/source/lib/dng_sdk/dng_gain_map.h new file mode 100644 index 0000000..f13cde1 --- /dev/null +++ b/source/lib/dng_sdk/dng_gain_map.h @@ -0,0 +1,218 @@ +/*****************************************************************************/ +// Copyright 2008-2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_gain_map.h#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * Opcode to fix 2D uniformity defects, such as shading. + */ + +/*****************************************************************************/ + +#ifndef __dng_gain_map__ +#define __dng_gain_map__ + +/*****************************************************************************/ + +#include "dng_memory.h" +#include "dng_misc_opcodes.h" +#include "dng_tag_types.h" + +/*****************************************************************************/ + +/// \brief Holds a discrete (i.e., sampled) 2D representation of a gain map. This is +/// effectively an image containing scale factors. + +class dng_gain_map + { + + private: + + dng_point fPoints; + + dng_point_real64 fSpacing; + + dng_point_real64 fOrigin; + + uint32 fPlanes; + + uint32 fRowStep; + + AutoPtr fBuffer; + + public: + + /// Construct a gain map with the specified memory allocator, number of + /// samples (points), sample spacing, origin, and number of color planes. + + dng_gain_map (dng_memory_allocator &allocator, + const dng_point &points, + const dng_point_real64 &spacing, + const dng_point_real64 &origin, + uint32 planes); + + /// The number of samples in the horizontal and vertical directions. + + const dng_point & Points () const + { + return fPoints; + } + + /// The space between adjacent samples in the horizontal and vertical + /// directions. + + const dng_point_real64 & Spacing () const + { + return fSpacing; + } + + /// The 2D coordinate for the first (i.e., top-left-most) sample. + + const dng_point_real64 & Origin () const + { + return fOrigin; + } + + /// The number of color planes. + + uint32 Planes () const + { + return fPlanes; + } + + /// Getter for a gain map sample (specified by row, column, and plane). + + real32 & Entry (uint32 rowIndex, + uint32 colIndex, + uint32 plane) + { + + return *(fBuffer->Buffer_real32 () + + rowIndex * fRowStep + + colIndex * fPlanes + + plane); + + } + + /// Getter for a gain map sample (specified by row index, column index, and + /// plane index). + + const real32 & Entry (uint32 rowIndex, + uint32 colIndex, + uint32 plane) const + { + + return *(fBuffer->Buffer_real32 () + + rowIndex * fRowStep + + colIndex * fPlanes + + plane); + + } + + /// Compute the interpolated gain (i.e., scale factor) at the specified pixel + /// position and color plane, within the specified image bounds (in pixels). + + real32 Interpolate (int32 row, + int32 col, + uint32 plane, + const dng_rect &bounds) const; + + /// The number of bytes needed to hold the gain map data. + + uint32 PutStreamSize () const; + + /// Write the gain map to the specified stream. + + void PutStream (dng_stream &stream) const; + + /// Read a gain map from the specified stream. + + static dng_gain_map * GetStream (dng_stream &stream, dng_memory_allocator &allocator); + + private: + + // Hidden copy constructor and assignment operator. + + dng_gain_map (const dng_gain_map &map); + + dng_gain_map & operator= (const dng_gain_map &map); + + }; + +/*****************************************************************************/ + +/// \brief An opcode to fix 2D spatially-varying light falloff or color casts (i.e., +/// uniformity issues). This is commonly due to shading. + +class dng_opcode_GainMap: public dng_inplace_opcode + { + + private: + + dng_area_spec fAreaSpec; + + AutoPtr fGainMap; + + public: + + /// Construct a GainMap opcode for the specified image area and the specified + /// gain map. + + dng_opcode_GainMap (const dng_area_spec &areaSpec, + AutoPtr &gainMap); + + /// Construct a GainMap opcode from the specified stream. + + dng_opcode_GainMap (dng_host &host, + dng_stream &stream); + + /// Write the opcode to the specified stream. + + virtual void PutData (dng_stream &stream) const; + + /// The pixel data type of this opcode. + + virtual uint32 BufferPixelType (uint32 /* imagePixelType */) + { + return ttFloat; + } + + /// The adjusted bounds (processing area) of this opcode. It is limited to + /// the intersection of the specified image area and the GainMap area. + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds) + { + return fAreaSpec.Overlap (imageBounds); + } + + /// Apply the gain map. + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + private: + + // Hidden copy constructor and assignment operator. + + dng_opcode_GainMap (const dng_opcode_GainMap &opcode); + + dng_opcode_GainMap & operator= (const dng_opcode_GainMap &opcode); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_globals.cpp b/source/lib/dng_sdk/dng_globals.cpp new file mode 100644 index 0000000..b279295 --- /dev/null +++ b/source/lib/dng_sdk/dng_globals.cpp @@ -0,0 +1,28 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_globals.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_globals.h" + +/*****************************************************************************/ + +#if qDNGValidate + +bool gVerbose = false; + +uint32 gDumpLineLimit = 100; + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_globals.h b/source/lib/dng_sdk/dng_globals.h new file mode 100644 index 0000000..ad48017 --- /dev/null +++ b/source/lib/dng_sdk/dng_globals.h @@ -0,0 +1,46 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_globals.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Definitions of global variables controling DNG SDK behavior. Currenntly only used for validation control. + */ + +/*****************************************************************************/ + +#ifndef __dng_globals__ +#define __dng_globals__ + +/*****************************************************************************/ + +#include "dng_flags.h" +#include "dng_types.h" + +/*****************************************************************************/ + +#if qDNGValidate + +/// When validation (qValidate) is turned on, this globale enables verbose output about DNG tags and other properties. + +extern bool gVerbose; + +/// When validation (qValidate) is turned on, and verbose mode (gVerbose) is enabled, limits the number of lines of text that are dumped for each tag. + +extern uint32 gDumpLineLimit; + +#endif + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_host.cpp b/source/lib/dng_sdk/dng_host.cpp new file mode 100644 index 0000000..91ebdc0 --- /dev/null +++ b/source/lib/dng_sdk/dng_host.cpp @@ -0,0 +1,539 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_host.cpp#2 $ */ +/* $DateTime: 2012/06/14 20:24:41 $ */ +/* $Change: 835078 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_host.h" + +#include "dng_abort_sniffer.h" +#include "dng_area_task.h" +#include "dng_bad_pixels.h" +#include "dng_exceptions.h" +#include "dng_exif.h" +#include "dng_gain_map.h" +#include "dng_ifd.h" +#include "dng_lens_correction.h" +#include "dng_memory.h" +#include "dng_misc_opcodes.h" +#include "dng_negative.h" +#include "dng_resample.h" +#include "dng_shared.h" +#include "dng_simple_image.h" +#include "dng_xmp.h" + +/*****************************************************************************/ + +dng_host::dng_host (dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer) + + : fAllocator (allocator) + , fSniffer (sniffer) + + , fNeedsMeta (true) + , fNeedsImage (true) + , fForPreview (false) + , fMinimumSize (0) + , fPreferredSize (0) + , fMaximumSize (0) + , fCropFactor (1.0) + , fSaveDNGVersion (dngVersion_None) + , fSaveLinearDNG (false) + , fKeepOriginalFile (false) + + { + + } + +/*****************************************************************************/ + +dng_host::~dng_host () + { + + } + +/*****************************************************************************/ + +dng_memory_allocator & dng_host::Allocator () + { + + if (fAllocator) + { + + return *fAllocator; + + } + + else + { + + return gDefaultDNGMemoryAllocator; + + } + + } + +/*****************************************************************************/ + +dng_memory_block * dng_host::Allocate (uint32 logicalSize) + { + + return Allocator ().Allocate (logicalSize); + + } + +/*****************************************************************************/ + +void dng_host::SniffForAbort () + { + + dng_abort_sniffer::SniffForAbort (Sniffer ()); + + } + +/*****************************************************************************/ + +void dng_host::ValidateSizes () + { + + // The maximum size limits the other two sizes. + + if (MaximumSize ()) + { + SetMinimumSize (Min_uint32 (MinimumSize (), MaximumSize ())); + SetPreferredSize (Min_uint32 (PreferredSize (), MaximumSize ())); + } + + // If we have a preferred size, it limits the minimum size. + + if (PreferredSize ()) + { + SetMinimumSize (Min_uint32 (MinimumSize (), PreferredSize ())); + } + + // Else find default value for preferred size. + + else + { + + // If preferred size is zero, then we want the maximim + // size image. + + if (MaximumSize ()) + { + SetPreferredSize (MaximumSize ()); + } + + } + + // If we don't have a minimum size, find default. + + if (!MinimumSize ()) + { + + // A common size for embedded thumbnails is 120 by 160 pixels, + // So allow 120 by 160 pixels to be used for thumbnails when the + // preferred size is 256 pixel. + + if (PreferredSize () >= 160 && PreferredSize () <= 256) + { + SetMinimumSize (160); + } + + // Many sensors are near a multiple of 1024 pixels in size, but after + // the default crop, they are a just under. We can get an extra factor + // of size reduction if we allow a slight undershoot in the final size + // when computing large previews. + + else if (PreferredSize () >= 490 && PreferredSize () <= 512) + { + SetMinimumSize (490); + } + + else if (PreferredSize () >= 980 && PreferredSize () <= 1024) + { + SetMinimumSize (980); + } + + else if (PreferredSize () >= 1470 && PreferredSize () <= 1536) + { + SetMinimumSize (1470); + } + + else if (PreferredSize () >= 1960 && PreferredSize () <= 2048) + { + SetMinimumSize (1960); + } + + // Else minimum size is same as preferred size. + + else + { + SetMinimumSize (PreferredSize ()); + } + + } + + } + +/*****************************************************************************/ + +uint32 dng_host::SaveDNGVersion () const + { + + return fSaveDNGVersion; + + } + +/*****************************************************************************/ + +bool dng_host::SaveLinearDNG (const dng_negative & /* negative */) const + { + + return fSaveLinearDNG; + + } + +/*****************************************************************************/ + +bool dng_host::IsTransientError (dng_error_code code) + { + + switch (code) + { + + case dng_error_memory: + case dng_error_user_canceled: + { + return true; + } + + default: + break; + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_host::PerformAreaTask (dng_area_task &task, + const dng_rect &area) + { + + dng_area_task::Perform (task, + area, + &Allocator (), + Sniffer ()); + + } + +/*****************************************************************************/ + +uint32 dng_host::PerformAreaTaskThreads () + { + + return 1; + + } + +/*****************************************************************************/ + +dng_exif * dng_host::Make_dng_exif () + { + + dng_exif *result = new dng_exif (); + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_xmp * dng_host::Make_dng_xmp () + { + + dng_xmp *result = new dng_xmp (Allocator ()); + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_shared * dng_host::Make_dng_shared () + { + + dng_shared *result = new dng_shared (); + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_ifd * dng_host::Make_dng_ifd () + { + + dng_ifd *result = new dng_ifd (); + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_negative * dng_host::Make_dng_negative () + { + + return dng_negative::Make (*this); + + } + +/*****************************************************************************/ + +dng_image * dng_host::Make_dng_image (const dng_rect &bounds, + uint32 planes, + uint32 pixelType) + { + + dng_image *result = new dng_simple_image (bounds, + planes, + pixelType, + Allocator ()); + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_opcode * dng_host::Make_dng_opcode (uint32 opcodeID, + dng_stream &stream) + { + + dng_opcode *result = NULL; + + switch (opcodeID) + { + + case dngOpcode_WarpRectilinear: + { + + result = new dng_opcode_WarpRectilinear (stream); + + break; + + } + + case dngOpcode_WarpFisheye: + { + + result = new dng_opcode_WarpFisheye (stream); + + break; + + } + + case dngOpcode_FixVignetteRadial: + { + + result = new dng_opcode_FixVignetteRadial (stream); + + break; + + } + + case dngOpcode_FixBadPixelsConstant: + { + + result = new dng_opcode_FixBadPixelsConstant (stream); + + break; + + } + + case dngOpcode_FixBadPixelsList: + { + + result = new dng_opcode_FixBadPixelsList (stream); + + break; + + } + + case dngOpcode_TrimBounds: + { + + result = new dng_opcode_TrimBounds (stream); + + break; + + } + + case dngOpcode_MapTable: + { + + result = new dng_opcode_MapTable (*this, + stream); + + break; + + } + + case dngOpcode_MapPolynomial: + { + + result = new dng_opcode_MapPolynomial (stream); + + break; + + } + + case dngOpcode_GainMap: + { + + result = new dng_opcode_GainMap (*this, + stream); + + break; + + } + + case dngOpcode_DeltaPerRow: + { + + result = new dng_opcode_DeltaPerRow (*this, + stream); + + break; + + } + + case dngOpcode_DeltaPerColumn: + { + + result = new dng_opcode_DeltaPerColumn (*this, + stream); + + break; + + } + + case dngOpcode_ScalePerRow: + { + + result = new dng_opcode_ScalePerRow (*this, + stream); + + break; + + } + + case dngOpcode_ScalePerColumn: + { + + result = new dng_opcode_ScalePerColumn (*this, + stream); + + break; + + } + + default: + { + + result = new dng_opcode_Unknown (*this, + opcodeID, + stream); + + } + + } + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_host::ApplyOpcodeList (dng_opcode_list &list, + dng_negative &negative, + AutoPtr &image) + { + + list.Apply (*this, + negative, + image); + + } + +/*****************************************************************************/ + +void dng_host::ResampleImage (const dng_image &srcImage, + dng_image &dstImage) + { + + ::ResampleImage (*this, + srcImage, + dstImage, + srcImage.Bounds (), + dstImage.Bounds (), + dng_resample_bicubic::Get ()); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_host.h b/source/lib/dng_sdk/dng_host.h new file mode 100644 index 0000000..3e82504 --- /dev/null +++ b/source/lib/dng_sdk/dng_host.h @@ -0,0 +1,411 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_host.h#2 $ */ +/* $DateTime: 2012/06/14 20:24:41 $ */ +/* $Change: 835078 $ */ +/* $Author: tknoll $ */ + +/** \file + * Class definition for dng_host, initial point of contact and control between + * host application and DNG SDK. + */ + +/*****************************************************************************/ + +#ifndef __dng_host__ +#define __dng_host__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_errors.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief The main class for communication between the application and the +/// DNG SDK. Used to customize memory allocation and other behaviors. +/// +/// dng_host allows setting parameters for the DNG conversion, mediates callback +/// style interactions between the host application and the DNG SDK, and allows +/// controlling certain internal behavior of the SDK such as memory allocation. +/// Many applications will be able to use the default implementation of dng_host +/// by just setting the dng_memory_allocator and dng_abort_sniffer in the +/// constructor. More complex interactions will require deriving a class from +/// dng_host. +/// +/// Multiple dng_host objects can be allocated in a single process. This may +/// be useful for DNG processing on separate threads. (Distinct dng_host objects +/// are completely threadsafe for read/write. The application is responsible for +/// establishing mutual exclusion for read/write access to a single dng_host +/// object if it is used in multiple threads.) + +class dng_host + { + + private: + + dng_memory_allocator *fAllocator; + + dng_abort_sniffer *fSniffer; + + // Does the host require all the image metadata (vs. just checking + // to see if the file is readable)? + + bool fNeedsMeta; + + // Does the host require actual image data (vs. just getting metadata + // or just checking to see if the file is readable)? + + bool fNeedsImage; + + // If we need the image data, can it be read at preview quality? + + bool fForPreview; + + // If non-zero, the minimum size (longer of the two pixel dimensions) + // image to read. If zero, or if the full size image is smaller than + // this, read the full size image. + + uint32 fMinimumSize; + + // What is the preferred size for a preview image? This can + // be slightly larger than the minimum size. Zero if we want + // the full resolution image. + + uint32 fPreferredSize; + + // What is the maximum size for a preview image? Zero if there + // is no maximum size limit. + + uint32 fMaximumSize; + + // The fraction of the image kept after a crop. This is used to + // adjust the sizes to take into account the cropping that + // will be peformed. + + real64 fCropFactor; + + // What DNG version should we keep enough data to save? + + uint32 fSaveDNGVersion; + + // Do we want to force saving to a linear DNG? + + bool fSaveLinearDNG; + + // Keep the original raw file data block? + + bool fKeepOriginalFile; + + // Payload for General Purpose Metadata Format (GPMF) + AutoPtr gpmf_payload; + + public: + + AutoPtr& GetGPMFPayload() { return gpmf_payload; }; + + /// Allocate a dng_host object, possiblly with custom allocator and sniffer. + /// \param allocator Allows controlling all memory allocation done via this + /// dng_host. Defaults to singleton global dng_memory_allocator, which calls + /// new/delete dng_malloc_block for appropriate size. + /// \param sniffer Used to periodically check if pending DNG conversions + /// should be aborted and to communicate progress updates. Defaults to singleton + /// global dng_abort_sniffer, which never aborts and ignores progress updated. + + dng_host (dng_memory_allocator *allocator = NULL, + dng_abort_sniffer *sniffer = NULL); + + /// Clean up direct memory for dng_host. Memory allocator and abort sniffer + /// are not deleted. Objects such as dng_image and others returned from + /// host can still be used after host is deleted. + + virtual ~dng_host (); + + /// Getter for host's memory allocator. + + dng_memory_allocator & Allocator (); + + /// Alocate a new dng_memory_block using the host's memory allocator. + /// Uses the Allocator() property of host to allocate a new block of memory. + /// Will call ThrowMemoryFull if block cannot be allocated. + /// \param logicalSize Number of usable bytes returned dng_memory_block + /// must contain. + + virtual dng_memory_block * Allocate (uint32 logicalSize); + + /// Setter for host's abort sniffer. + + void SetSniffer (dng_abort_sniffer *sniffer) + { + fSniffer = sniffer; + } + + /// Getter for host's abort sniffer. + + dng_abort_sniffer * Sniffer () + { + return fSniffer; + } + + /// Check for pending abort. Should call ThrowUserCanceled if an abort + /// is pending. + + virtual void SniffForAbort (); + + /// Setter for flag determining whether all XMP metadata should be parsed. + /// Defaults to true. One might not want metadata when doing a quick check + /// to see if a file is readable. + /// \param needs If true, metadata is needed. + + void SetNeedsMeta (bool needs) + { + fNeedsMeta = needs; + } + + /// Getter for flag determining whether all XMP metadata should be parsed. + + bool NeedsMeta () const + { + return fNeedsMeta; + } + + /// Setter for flag determining whether DNG image data is needed. Defaults + /// to true. Image data might not be needed for applications which only + /// manipulate metadata. + /// \param needs If true, image data is needed. + + void SetNeedsImage (bool needs) + { + fNeedsImage = needs; + } + + /// Setter for flag determining whether DNG image data is needed. + + bool NeedsImage () const + { + return fNeedsImage; + } + + /// Setter for flag determining whether image should be preview quality, + /// or full quality. + /// \param preview If true, rendered images are for preview. + + void SetForPreview (bool preview) + { + fForPreview = preview; + } + + /// Getter for flag determining whether image should be preview quality. + /// Preview quality images may be rendered more quickly. Current DNG SDK + /// does not change rendering behavior based on this flag, but derived + /// versions may use this getter to choose between a slower more accurate path + /// and a faster "good enough for preview" one. Data produce with ForPreview set + /// to true should not be written back to a DNG file, except as a preview image. + + bool ForPreview () const + { + return fForPreview; + } + + /// Setter for the minimum preview size. + /// \param size Minimum pixel size (long side of image). + + void SetMinimumSize (uint32 size) + { + fMinimumSize = size; + } + + /// Getter for the minimum preview size. + + uint32 MinimumSize () const + { + return fMinimumSize; + } + + /// Setter for the preferred preview size. + /// \param size Preferred pixel size (long side of image). + + void SetPreferredSize (uint32 size) + { + fPreferredSize = size; + } + + /// Getter for the preferred preview size. + + uint32 PreferredSize () const + { + return fPreferredSize; + } + + /// Setter for the maximum preview size. + /// \param size Maximum pixel size (long side of image). + + void SetMaximumSize (uint32 size) + { + fMaximumSize = size; + } + + /// Getter for the maximum preview size. + + uint32 MaximumSize () const + { + return fMaximumSize; + } + + /// Setter for the cropping factor. + /// \param cropFactor Fraction of image to be used after crop. + + void SetCropFactor (real64 cropFactor) + { + fCropFactor = cropFactor; + } + + /// Getter for the cropping factor. + + real64 CropFactor () const + { + return fCropFactor; + } + + /// Makes sures minimum, preferred, and maximum sizes are reasonable. + + void ValidateSizes (); + + /// Setter for what version to save DNG file compatible with. + /// \param version What version to save DNG file compatible with. + + void SetSaveDNGVersion (uint32 version) + { + fSaveDNGVersion = version; + } + + /// Getter for what version to save DNG file compatible with. + + virtual uint32 SaveDNGVersion () const; + + /// Setter for flag determining whether to force saving a linear DNG file. + /// \param linear If true, we should force saving a linear DNG file. + + void SetSaveLinearDNG (bool linear) + { + fSaveLinearDNG = linear; + } + + /// Getter for flag determining whether to save a linear DNG file. + + virtual bool SaveLinearDNG (const dng_negative &negative) const; + + /// Setter for flag determining whether to keep original RAW file data. + /// \param keep If true, origianl RAW data will be kept. + + void SetKeepOriginalFile (bool keep) + { + fKeepOriginalFile = keep; + } + + /// Getter for flag determining whether to keep original RAW file data. + + bool KeepOriginalFile () + { + return fKeepOriginalFile; + } + + /// Determine if an error is the result of a temporary, but planned-for + /// occurence such as user cancellation or memory exhaustion. This method is + /// sometimes used to determine whether to try and continue processing a DNG + /// file despite errors in the file format, etc. In such cases, processing will + /// be continued if IsTransientError returns false. This is so that user cancellation + /// and memory exhaustion always terminate processing. + /// \param code Error to test for transience. + + virtual bool IsTransientError (dng_error_code code); + + /// General top-level botttleneck for image processing tasks. + /// Default implementation calls dng_area_task::PerformAreaTask method on + /// task. Can be overridden in derived classes to support multiprocessing, + /// for example. + /// \param task Image processing task to perform on area. + /// \param area Rectangle over which to perform image processing task. + + virtual void PerformAreaTask (dng_area_task &task, + const dng_rect &area); + + /// How many multiprocessing threads does PerformAreaTask use? + /// Default implementation always returns 1 since it is single threaded. + + virtual uint32 PerformAreaTaskThreads (); + + /// Factory method for dng_exif class. Can be used to customize allocation or + /// to ensure a derived class is used instead of dng_exif. + + virtual dng_exif * Make_dng_exif (); + + /// Factory method for dng_xmp class. Can be used to customize allocation or + /// to ensure a derived class is used instead of dng_xmp. + + virtual dng_xmp * Make_dng_xmp (); + + /// Factory method for dng_shared class. Can be used to customize allocation + /// or to ensure a derived class is used instead of dng_shared. + + virtual dng_shared * Make_dng_shared (); + + /// Factory method for dng_ifd class. Can be used to customize allocation or + /// to ensure a derived class is used instead of dng_ifd. + + virtual dng_ifd * Make_dng_ifd (); + + /// Factory method for dng_negative class. Can be used to customize allocation + /// or to ensure a derived class is used instead of dng_negative. + + virtual dng_negative * Make_dng_negative (); + + /// Factory method for dng_image class. Can be used to customize allocation + /// or to ensure a derived class is used instead of dng_simple_image. + + virtual dng_image * Make_dng_image (const dng_rect &bounds, + uint32 planes, + uint32 pixelType); + + /// Factory method for parsing dng_opcode based classs. Can be used to + /// override opcode implementations. + + virtual dng_opcode * Make_dng_opcode (uint32 opcodeID, + dng_stream &stream); + + /// Factory method to apply a dng_opcode_list. Can be used to override + /// opcode list applications. + + virtual void ApplyOpcodeList (dng_opcode_list &list, + dng_negative &negative, + AutoPtr &image); + + /// Factory method to resample an image. Can be used to override + /// image method used to resample images. + + virtual void ResampleImage (const dng_image &srcImage, + dng_image &dstImage); + + private: + + // Hidden copy constructor and assignment operator. + + dng_host (const dng_host &host); + + dng_host & operator= (const dng_host &host); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_hue_sat_map.cpp b/source/lib/dng_sdk/dng_hue_sat_map.cpp new file mode 100644 index 0000000..8fddcca --- /dev/null +++ b/source/lib/dng_sdk/dng_hue_sat_map.cpp @@ -0,0 +1,367 @@ +/*****************************************************************************/ +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_hue_sat_map.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_hue_sat_map.h" + +#include "dng_assertions.h" +#include "dng_auto_ptr.h" +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_host.h" + +#include "stdc_includes.h" + +/*****************************************************************************/ + +dng_hue_sat_map::dng_hue_sat_map () + + : fHueDivisions (0) + , fSatDivisions (0) + , fValDivisions (0) + , fHueStep (0) + , fValStep (0) + , fDeltas () + + { + + } + +/*****************************************************************************/ + +dng_hue_sat_map::dng_hue_sat_map (const dng_hue_sat_map &src) + + : fHueDivisions (0) + , fSatDivisions (0) + , fValDivisions (0) + , fHueStep (0) + , fValStep (0) + , fDeltas () + + { + + *this = src; + + } + +/*****************************************************************************/ + +dng_hue_sat_map &dng_hue_sat_map::operator= (const dng_hue_sat_map &rhs) + { + + if (this != &rhs) + { + + if (!rhs.IsValid ()) + { + + SetInvalid (); + + } + + else + { + + fHueDivisions = rhs.fHueDivisions; + fSatDivisions = rhs.fSatDivisions; + fValDivisions = rhs.fValDivisions; + + fHueStep = rhs.fHueStep; + fValStep = rhs.fValStep; + + fDeltas = rhs.fDeltas; + + } + + } + + return *this; + + } + +/*****************************************************************************/ + +dng_hue_sat_map::~dng_hue_sat_map () + { + + } + +/*****************************************************************************/ + +void dng_hue_sat_map::SetDivisions (uint32 hueDivisions, + uint32 satDivisions, + uint32 valDivisions) + { + + DNG_ASSERT (hueDivisions >= 1, "Must have at least 1 hue division."); + DNG_ASSERT (satDivisions >= 2, "Must have at least 2 sat divisions."); + + if (valDivisions == 0) + valDivisions = 1; + + if (hueDivisions == fHueDivisions && + satDivisions == fSatDivisions && + valDivisions == fValDivisions) + { + return; + } + + fHueDivisions = hueDivisions; + fSatDivisions = satDivisions; + fValDivisions = valDivisions; + + fHueStep = satDivisions; + fValStep = hueDivisions * fHueStep; + + uint32 size = DeltasCount () * (uint32) sizeof (HSBModify); + + fDeltas.Allocate (size); + + DoZeroBytes (fDeltas.Buffer (), size); + + } + +/*****************************************************************************/ + +void dng_hue_sat_map::GetDelta (uint32 hueDiv, + uint32 satDiv, + uint32 valDiv, + HSBModify &modify) const + { + + if (hueDiv >= fHueDivisions || + satDiv >= fSatDivisions || + valDiv >= fValDivisions || + fDeltas.Buffer () == NULL) + { + + DNG_REPORT ("Bad parameters to dng_hue_sat_map::GetDelta"); + + ThrowProgramError (); + + } + + int32 offset = valDiv * fValStep + + hueDiv * fHueStep + + satDiv; + + const HSBModify *deltas = GetConstDeltas (); + + modify.fHueShift = deltas [offset].fHueShift; + modify.fSatScale = deltas [offset].fSatScale; + modify.fValScale = deltas [offset].fValScale; + + } + +/*****************************************************************************/ + +void dng_hue_sat_map::SetDeltaKnownWriteable (uint32 hueDiv, + uint32 satDiv, + uint32 valDiv, + const HSBModify &modify) + { + + if (hueDiv >= fHueDivisions || + satDiv >= fSatDivisions || + valDiv >= fValDivisions || + fDeltas.Buffer () == NULL) + { + + DNG_REPORT ("Bad parameters to dng_hue_sat_map::SetDelta"); + + ThrowProgramError (); + + } + + // Set this entry. + + int32 offset = valDiv * fValStep + + hueDiv * fHueStep + + satDiv; + + SafeGetDeltas () [offset] = modify; + + // The zero saturation entry is required to have a value scale + // of 1.0f. + + if (satDiv == 0) + { + + if (modify.fValScale != 1.0f) + { + + #if qDNGValidate + + ReportWarning ("Value scale for zero saturation entries must be 1.0"); + + #endif + + SafeGetDeltas () [offset] . fValScale = 1.0f; + + } + + } + + // If we are settings the first saturation entry and we have not + // set the zero saturation entry yet, fill in the zero saturation entry + // by extrapolating first saturation entry. + + if (satDiv == 1) + { + + HSBModify zeroSatModify; + + GetDelta (hueDiv, 0, valDiv, zeroSatModify); + + if (zeroSatModify.fValScale != 1.0f) + { + + zeroSatModify.fHueShift = modify.fHueShift; + zeroSatModify.fSatScale = modify.fSatScale; + zeroSatModify.fValScale = 1.0f; + + SetDelta (hueDiv, 0, valDiv, zeroSatModify); + + } + + } + + } + +/*****************************************************************************/ + +bool dng_hue_sat_map::operator== (const dng_hue_sat_map &rhs) const + { + + if (fHueDivisions != rhs.fHueDivisions || + fSatDivisions != rhs.fSatDivisions || + fValDivisions != rhs.fValDivisions) + return false; + + if (!IsValid ()) + return true; + + return memcmp (GetConstDeltas (), + rhs.GetConstDeltas (), + DeltasCount () * sizeof (HSBModify)) == 0; + + } + +/*****************************************************************************/ + +dng_hue_sat_map * dng_hue_sat_map::Interpolate (const dng_hue_sat_map &map1, + const dng_hue_sat_map &map2, + real64 weight1) + { + + if (weight1 >= 1.0) + { + + if (!map1.IsValid ()) + { + + DNG_REPORT ("map1 is not valid"); + + ThrowProgramError (); + + } + + return new dng_hue_sat_map (map1); + + } + + if (weight1 <= 0.0) + { + + if (!map2.IsValid ()) + { + DNG_REPORT ("map2 is not valid"); + + ThrowProgramError (); + + } + + return new dng_hue_sat_map (map2); + + } + + // Both maps must be valid if we are using both. + + if (!map1.IsValid () || !map2.IsValid ()) + { + + DNG_REPORT ("map1 or map2 is not valid"); + + ThrowProgramError (); + + } + + // Must have the same dimensions. + + if (map1.fHueDivisions != map2.fHueDivisions || + map1.fSatDivisions != map2.fSatDivisions || + map1.fValDivisions != map2.fValDivisions) + { + + DNG_REPORT ("map1 and map2 have different sizes"); + + ThrowProgramError (); + + } + + // Make table to hold interpolated results. + + AutoPtr result (new dng_hue_sat_map); + + result->SetDivisions (map1.fHueDivisions, + map1.fSatDivisions, + map1.fValDivisions); + + // Interpolate between the tables. + + real32 w1 = (real32) weight1; + real32 w2 = 1.0f - w1; + + const HSBModify *data1 = map1.GetConstDeltas (); + const HSBModify *data2 = map2.GetConstDeltas (); + + HSBModify *data3 = result->SafeGetDeltas (); + + uint32 count = map1.DeltasCount (); + + for (uint32 index = 0; index < count; index++) + { + + data3->fHueShift = w1 * data1->fHueShift + + w2 * data2->fHueShift; + + data3->fSatScale = w1 * data1->fSatScale + + w2 * data2->fSatScale; + + data3->fValScale = w1 * data1->fValScale + + w2 * data2->fValScale; + + data1++; + data2++; + data3++; + + } + + // Return interpolated tables. + + return result.Release (); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_hue_sat_map.h b/source/lib/dng_sdk/dng_hue_sat_map.h new file mode 100644 index 0000000..c0816e2 --- /dev/null +++ b/source/lib/dng_sdk/dng_hue_sat_map.h @@ -0,0 +1,229 @@ +/*****************************************************************************/ +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_hue_sat_map.h#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * Table-based color correction data structure. + */ + +/*****************************************************************************/ + +#ifndef __dng_hue_sat_map__ +#define __dng_hue_sat_map__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_ref_counted_block.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief A 3D table that maps HSV (hue, saturation, and value) floating-point +/// input coordinates in the range [0,1] to delta signals. The table must have at +/// least 1 sample in the hue dimension, at least 2 samples in the saturation +/// dimension, and at least 1 sample in the value dimension. Tables are stored in +/// value-hue-saturation order. + +class dng_hue_sat_map + { + + public: + + /// HSV delta signal. \param fHueShift is a delta value specified in degrees. + /// This parameter, added to the original hue, determines the output hue. A + /// value of 0 means no change. \param fSatScale and \param fValScale are + /// scale factors that are applied to saturation and value components, + /// respectively. These scale factors, multiplied by the original saturation + /// and value, determine the output saturation and value. A scale factor of + /// 1.0 means no change. + + struct HSBModify + { + real32 fHueShift; + real32 fSatScale; + real32 fValScale; + }; + + private: + + uint32 fHueDivisions; + uint32 fSatDivisions; + uint32 fValDivisions; + + uint32 fHueStep; + uint32 fValStep; + + dng_ref_counted_block fDeltas; + + HSBModify *SafeGetDeltas () + { + return (HSBModify *) fDeltas.Buffer_real32 (); + } + + public: + + /// Construct an empty (and invalid) hue sat map. + + dng_hue_sat_map (); + + /// Copy an existing hue sat map. + + dng_hue_sat_map (const dng_hue_sat_map &src); + + /// Copy an existing hue sat map. + + dng_hue_sat_map & operator= (const dng_hue_sat_map &rhs); + + /// Destructor. + + virtual ~dng_hue_sat_map (); + + /// Is this hue sat map invalid? + + bool IsNull () const + { + return !IsValid (); + } + + /// Is this hue sat map valid? + + bool IsValid () const + { + + return fHueDivisions > 0 && + fSatDivisions > 1 && + fValDivisions > 0 && + fDeltas.Buffer (); + + } + + /// Clear the hue sat map, making it invalid. + + void SetInvalid () + { + + fHueDivisions = 0; + fSatDivisions = 0; + fValDivisions = 0; + + fHueStep = 0; + fValStep = 0; + + fDeltas.Clear (); + + } + + /// Get the table dimensions (number of samples in each dimension). + + void GetDivisions (uint32 &hueDivisions, + uint32 &satDivisions, + uint32 &valDivisions) const + { + hueDivisions = fHueDivisions; + satDivisions = fSatDivisions; + valDivisions = fValDivisions; + } + + /// Set the table dimensions (number of samples in each dimension). This + /// erases any existing table data. + + void SetDivisions (uint32 hueDivisions, + uint32 satDivisions, + uint32 valDivisions = 1); + + /// Get a specific table entry, specified by table indices. + + void GetDelta (uint32 hueDiv, + uint32 satDiv, + uint32 valDiv, + HSBModify &modify) const; + + /// Make sure the table is writeable. + + void EnsureWriteable () + { + fDeltas.EnsureWriteable (); + } + + /// Set a specific table entry, specified by table indices. + + void SetDelta (uint32 hueDiv, + uint32 satDiv, + uint32 valDiv, + const HSBModify &modify) + { + + EnsureWriteable (); + + SetDeltaKnownWriteable (hueDiv, + satDiv, + valDiv, + modify); + + } + + /// Same as SetDelta, without checking that the table is writeable. + + void SetDeltaKnownWriteable (uint32 hueDiv, + uint32 satDiv, + uint32 valDiv, + const HSBModify &modify); + + /// Get the total number of samples (across all dimensions). + + uint32 DeltasCount () const + { + return fValDivisions * + fHueDivisions * + fSatDivisions; + } + + /// Direct read/write access to table entries. The entries are stored in + /// value-hue-saturation order (outer to inner). + + HSBModify *GetDeltas () + { + + EnsureWriteable (); + + return (HSBModify *) fDeltas.Buffer_real32 (); + + } + + /// Direct read-only access to table entries. The entries are stored in + /// value-hue-saturation order (outer to inner). + + const HSBModify *GetConstDeltas () const + { + return (const HSBModify *) fDeltas.Buffer_real32 (); + } + + /// Equality test. + + bool operator== (const dng_hue_sat_map &rhs) const; + + /// Compute a linearly-interpolated hue sat map (i.e., delta and scale factors) + /// from the specified tables, with the specified weight. map1 and map2 must + /// have the same dimensions. + + static dng_hue_sat_map * Interpolate (const dng_hue_sat_map &map1, + const dng_hue_sat_map &map2, + real64 weight1); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_ifd.cpp b/source/lib/dng_sdk/dng_ifd.cpp new file mode 100644 index 0000000..949635d --- /dev/null +++ b/source/lib/dng_sdk/dng_ifd.cpp @@ -0,0 +1,4266 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_ifd.cpp#3 $ */ +/* $DateTime: 2012/06/05 11:05:39 $ */ +/* $Change: 833352 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_ifd.h" + +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_globals.h" +#include "dng_ifd.h" +#include "dng_types.h" +#include "dng_parse_utils.h" +#include "dng_read_image.h" +#include "dng_stream.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_preview_info::dng_preview_info () + + : fIsPrimary (true) + , fApplicationName () + , fApplicationVersion () + , fSettingsName () + , fSettingsDigest () + , fColorSpace (previewColorSpace_MaxEnum) + , fDateTime () + , fRawToPreviewGain (1.0) + , fCacheVersion (0) + + { + + } + +/*****************************************************************************/ + +dng_preview_info::~dng_preview_info () + { + + } + +/*****************************************************************************/ + +dng_ifd::dng_ifd () + + : fUsesNewSubFileType (false) + , fNewSubFileType (0) + + , fImageWidth (0) + , fImageLength (0) + + , fCompression (ccUncompressed) + , fPredictor (cpNullPredictor) + + , fPhotometricInterpretation (0xFFFFFFFF) + + , fFillOrder (1) + + , fOrientation (0) + , fOrientationType (0) + , fOrientationOffset (kDNGStreamInvalidOffset) + , fOrientationBigEndian (false) + + , fSamplesPerPixel (1) + + , fPlanarConfiguration (pcInterleaved) + + , fXResolution (0.0) + , fYResolution (0.0) + , fResolutionUnit (0) + + , fUsesStrips (false) + , fUsesTiles (false) + + , fTileWidth (0) + , fTileLength (0) + + , fTileOffsetsType (0) + , fTileOffsetsCount (0) + , fTileOffsetsOffset (0) + + , fTileByteCountsType (0) + , fTileByteCountsCount (0) + , fTileByteCountsOffset (0) + + , fSubIFDsCount (0) + , fSubIFDsOffset (0) + + , fExtraSamplesCount (0) + + , fJPEGTablesCount (0) + , fJPEGTablesOffset (0) + + , fJPEGInterchangeFormat (0) + , fJPEGInterchangeFormatLength (0) + + , fYCbCrCoefficientR (0.0) + , fYCbCrCoefficientG (0.0) + , fYCbCrCoefficientB (0.0) + + , fYCbCrSubSampleH (0) + , fYCbCrSubSampleV (0) + + , fYCbCrPositioning (0) + + , fCFARepeatPatternRows (0) + , fCFARepeatPatternCols (0) + + , fCFALayout (1) + + , fLinearizationTableType (0) + , fLinearizationTableCount (0) + , fLinearizationTableOffset (0) + + , fBlackLevelRepeatRows (1) + , fBlackLevelRepeatCols (1) + + , fBlackLevelDeltaHType (0) + , fBlackLevelDeltaHCount (0) + , fBlackLevelDeltaHOffset (0) + + , fBlackLevelDeltaVType (0) + , fBlackLevelDeltaVCount (0) + , fBlackLevelDeltaVOffset (0) + + , fDefaultScaleH (1, 1) + , fDefaultScaleV (1, 1) + + , fBestQualityScale (1, 1) + + , fDefaultCropOriginH (0, 1) + , fDefaultCropOriginV (0, 1) + + , fDefaultCropSizeH () + , fDefaultCropSizeV () + + , fDefaultUserCropT (0, 1) + , fDefaultUserCropL (0, 1) + , fDefaultUserCropB (1, 1) + , fDefaultUserCropR (1, 1) + + , fBayerGreenSplit (0) + + , fChromaBlurRadius () + + , fAntiAliasStrength (1, 1) + + , fActiveArea () + + , fMaskedAreaCount (0) + + , fRowInterleaveFactor (1) + + , fSubTileBlockRows (1) + , fSubTileBlockCols (1) + + , fPreviewInfo () + + , fOpcodeList1Count (0) + , fOpcodeList1Offset (0) + + , fOpcodeList2Count (0) + , fOpcodeList2Offset (0) + + , fOpcodeList3Count (0) + , fOpcodeList3Offset (0) + + , fLosslessJPEGBug16 (false) + + , fSampleBitShift (0) + + , fThisIFD (0) + , fNextIFD (0) + + , fCompressionQuality (-1) + + , fPatchFirstJPEGByte (false) + + { + + uint32 j; + uint32 k; + uint32 n; + + for (j = 0; j < kMaxSamplesPerPixel; j++) + { + fBitsPerSample [j] = 0; + } + + for (j = 0; j < kMaxTileInfo; j++) + { + fTileOffset [j] = 0; + fTileByteCount [j] = 0; + } + + for (j = 0; j < kMaxSamplesPerPixel; j++) + { + fExtraSamples [j] = esUnspecified; + } + + for (j = 0; j < kMaxSamplesPerPixel; j++) + { + fSampleFormat [j] = sfUnsignedInteger; + } + + for (j = 0; j < 6; j++) + { + fReferenceBlackWhite [j] = 0.0; + } + + for (j = 0; j < kMaxCFAPattern; j++) + for (k = 0; k < kMaxCFAPattern; k++) + { + fCFAPattern [j] [k] = 255; + } + + for (j = 0; j < kMaxColorPlanes; j++) + { + fCFAPlaneColor [j] = (uint8) (j < 3 ? j : 255); + } + + for (j = 0; j < kMaxBlackPattern; j++) + for (k = 0; k < kMaxBlackPattern; k++) + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + fBlackLevel [j] [k] [n] = 0.0; + } + + for (j = 0; j < kMaxSamplesPerPixel; j++) + { + fWhiteLevel [j] = -1.0; // Don't know real default yet. + } + + } + +/*****************************************************************************/ + +dng_ifd::~dng_ifd () + { + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in IFDs that contain images. + +bool dng_ifd::ParseTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset) + { + + uint32 j; + uint32 k; + uint32 n; + + switch (tagCode) + { + + case tcNewSubFileType: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fUsesNewSubFileType = true; + + fNewSubFileType = stream.TagValue_uint32 (tagType); + + fPreviewInfo.fIsPrimary = (fNewSubFileType == sfPreviewImage); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("NewSubFileType: %s\n", + LookupNewSubFileType (fNewSubFileType)); + + } + + #endif + + break; + + } + + case tcImageWidth: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fImageWidth = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("ImageWidth: %u\n", (unsigned) fImageWidth); + } + + #endif + + break; + + } + + case tcImageLength: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fImageLength = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("ImageLength: %u\n", (unsigned) fImageLength); + } + + #endif + + break; + + } + + case tcBitsPerSample: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1, 0x0FFFF); + + #if qDNGValidate + + if (gVerbose) + { + printf ("BitsPerSample:"); + } + + #endif + + bool extrasMatch = true; + + for (j = 0; j < tagCount; j++) + { + + uint32 x = stream.TagValue_uint32 (tagType); + + if (j < kMaxSamplesPerPixel) + { + fBitsPerSample [j] = x; + } + + else if (x != fBitsPerSample [kMaxSamplesPerPixel - 1]) + { + extrasMatch = false; + } + + #if qDNGValidate + + if (gVerbose) + { + printf (" %u", (unsigned) x); + } + + #endif + + } + + #if qDNGValidate + + if (gVerbose) + { + printf ("\n"); + } + + #endif + + if (!extrasMatch) + { + + #if qDNGValidate + + ReportError ("BitsPerSample not constant"); + + #endif + + ThrowBadFormat (); + + } + + break; + + } + + case tcCompression: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCompression = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Compression: %s\n", + LookupCompression (fCompression)); + + } + + #endif + + // Correct a common TIFF writer mistake. + + if (fCompression == 0) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s has invalid zero compression code", + LookupParentCode (parentCode)); + + ReportWarning (message); + + } + + #endif + + fCompression = ccUncompressed; + + } + + break; + + } + + case tcPhotometricInterpretation: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPhotometricInterpretation = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PhotometricInterpretation: %s\n", + LookupPhotometricInterpretation (fPhotometricInterpretation)); + + } + + #endif + + break; + + } + + case tcFillOrder: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fFillOrder = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("FillOrder: %u\n", (unsigned) fFillOrder); + } + + #endif + + break; + + } + + case tcStripOffsets: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + fUsesStrips = true; + + fTileOffsetsType = tagType; + fTileOffsetsCount = tagCount; + fTileOffsetsOffset = tagOffset; + + if (tagCount <= kMaxTileInfo) + { + + for (j = 0; j < tagCount; j++) + { + + fTileOffset [j] = stream.TagValue_uint32 (tagType); + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + stream.SetReadPosition (tagOffset); + + DumpTagValues (stream, + "Offset", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcOrientation: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fOrientationType = tagType; + fOrientationOffset = stream.PositionInOriginalFile (); + fOrientationBigEndian = stream.BigEndian (); + + fOrientation = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Orientation: %s\n", + LookupOrientation (fOrientation)); + + } + + #endif + + break; + + } + + case tcSamplesPerPixel: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fSamplesPerPixel = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("SamplesPerPixel: %u\n", (unsigned) fSamplesPerPixel); + } + + #endif + + break; + + } + + case tcRowsPerStrip: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fUsesStrips = true; + + fTileLength = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("RowsPerStrip: %u\n", (unsigned) fTileLength); + } + + #endif + + break; + + } + + case tcStripByteCounts: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + fUsesStrips = true; + + fTileByteCountsType = tagType; + fTileByteCountsCount = tagCount; + fTileByteCountsOffset = tagOffset; + + if (tagCount <= kMaxTileInfo) + { + + for (j = 0; j < tagCount; j++) + { + + fTileByteCount [j] = stream.TagValue_uint32 (tagType); + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + stream.SetReadPosition (tagOffset); + + DumpTagValues (stream, + "Count", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcXResolution: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fXResolution = stream.TagValue_real64 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("XResolution: %0.2f\n", fXResolution); + } + + #endif + + break; + + } + + case tcYResolution: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fYResolution = stream.TagValue_real64 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("YResolution: %0.2f\n", fYResolution); + } + + #endif + + break; + + } + + case tcPlanarConfiguration: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPlanarConfiguration = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("PlanarConfiguration: %u\n", (unsigned) fPlanarConfiguration); + } + + #endif + + break; + + } + + case tcResolutionUnit: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fResolutionUnit = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ResolutionUnit: %s\n", + LookupResolutionUnit (fResolutionUnit)); + + } + + #endif + + break; + + } + + case tcPredictor: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPredictor = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Predictor: %s\n", + LookupPredictor (fPredictor)); + + } + + #endif + + break; + + } + + case tcTileWidth: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fUsesTiles = true; + + fTileWidth = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("TileWidth: %u\n", (unsigned) fTileWidth); + } + + #endif + + break; + + } + + case tcTileLength: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fUsesTiles = true; + + fTileLength = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("TileLength: %u\n", (unsigned) fTileLength); + } + + #endif + + break; + + } + + case tcTileOffsets: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + fUsesTiles = true; + + fTileOffsetsType = tagType; + fTileOffsetsCount = tagCount; + fTileOffsetsOffset = tagOffset; + + if (tagCount <= kMaxTileInfo) + { + + for (j = 0; j < tagCount; j++) + { + + fTileOffset [j] = stream.TagValue_uint32 (tagType); + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + stream.SetReadPosition (tagOffset); + + DumpTagValues (stream, + "Offset", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcTileByteCounts: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + fUsesTiles = true; + + fTileByteCountsType = tagType; + fTileByteCountsCount = tagCount; + fTileByteCountsOffset = tagOffset; + + if (tagCount <= kMaxTileInfo) + { + + for (j = 0; j < tagCount; j++) + { + + fTileByteCount [j] = stream.TagValue_uint32 (tagType); + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + stream.SetReadPosition (tagOffset); + + DumpTagValues (stream, + "Count", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcSubIFDs: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); + + fSubIFDsCount = tagCount; + fSubIFDsOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + DumpTagValues (stream, + "IFD", + parentCode, + tagCode, + ttLong, + tagCount); + + } + + #endif + + break; + + } + + case tcExtraSamples: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1, fSamplesPerPixel); + + #if qDNGValidate + + if (gVerbose) + { + printf ("ExtraSamples:"); + } + + #endif + + fExtraSamplesCount = tagCount; + + for (j = 0; j < tagCount; j++) + { + + uint32 x = stream.TagValue_uint32 (tagType); + + if (j < kMaxSamplesPerPixel) + { + fExtraSamples [j] = x; + } + + #if qDNGValidate + + if (gVerbose) + { + printf (" %u", (unsigned) x); + } + + #endif + + } + + #if qDNGValidate + + if (gVerbose) + { + printf ("\n"); + } + + #endif + + break; + + } + + case tcSampleFormat: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, fSamplesPerPixel); + + #if qDNGValidate + + if (gVerbose) + { + printf ("SampleFormat:"); + } + + #endif + + bool extrasMatch = true; + + for (j = 0; j < tagCount; j++) + { + + uint32 x = stream.TagValue_uint32 (tagType); + + if (j < kMaxSamplesPerPixel) + { + fSampleFormat [j] = x; + } + + else if (x != fSampleFormat [kMaxSamplesPerPixel - 1]) + { + extrasMatch = false; + } + + #if qDNGValidate + + if (gVerbose) + { + printf (" %s", LookupSampleFormat (x)); + } + + #endif + + } + + #if qDNGValidate + + if (gVerbose) + { + printf ("\n"); + } + + #endif + + if (!extrasMatch) + { + + #if qDNGValidate + + ReportError ("SampleFormat not constant"); + + #endif + + ThrowBadFormat (); + + } + + break; + + } + + case tcJPEGTables: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fJPEGTablesCount = tagCount; + fJPEGTablesOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("JPEGTables: count = %u, offset = %u\n", + (unsigned) fJPEGTablesCount, + (unsigned) fJPEGTablesOffset); + + } + + #endif + + break; + + } + + case tcJPEGInterchangeFormat: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fJPEGInterchangeFormat = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("JPEGInterchangeFormat: %u\n", + (unsigned) fJPEGInterchangeFormat); + } + + #endif + + break; + + } + + case tcJPEGInterchangeFormatLength: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fJPEGInterchangeFormatLength = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("JPEGInterchangeFormatLength: %u\n", + (unsigned) fJPEGInterchangeFormatLength); + } + + #endif + + break; + + } + + case tcYCbCrCoefficients: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 3)) + { + return false; + } + + fYCbCrCoefficientR = stream.TagValue_real64 (tagType); + fYCbCrCoefficientG = stream.TagValue_real64 (tagType); + fYCbCrCoefficientB = stream.TagValue_real64 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("YCbCrCoefficients: R = %0.3f, G = %0.3f, B = %0.3f\n", + fYCbCrCoefficientR, + fYCbCrCoefficientG, + fYCbCrCoefficientB); + + } + + #endif + + break; + + } + + case tcYCbCrSubSampling: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + { + return false; + } + + fYCbCrSubSampleH = stream.TagValue_uint32 (tagType); + fYCbCrSubSampleV = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("YCbCrSubSampling: H = %u, V = %u\n", + (unsigned) fYCbCrSubSampleH, + (unsigned) fYCbCrSubSampleV); + + } + + #endif + + break; + + } + + case tcYCbCrPositioning: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fYCbCrPositioning = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("YCbCrPositioning: %u\n", + (unsigned) fYCbCrPositioning); + + } + + #endif + + break; + + } + + case tcReferenceBlackWhite: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 6)) + { + return false; + } + + for (j = 0; j < 6; j++) + { + fReferenceBlackWhite [j] = stream.TagValue_real64 (tagType); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ReferenceBlackWhite: %0.1f %0.1f %0.1f %0.1f %0.1f %0.1f\n", + fReferenceBlackWhite [0], + fReferenceBlackWhite [1], + fReferenceBlackWhite [2], + fReferenceBlackWhite [3], + fReferenceBlackWhite [4], + fReferenceBlackWhite [5]); + + } + + #endif + + break; + + } + + case tcCFARepeatPatternDim: + { + + CheckCFA (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + { + return false; + } + + fCFARepeatPatternRows = stream.TagValue_uint32 (tagType); + fCFARepeatPatternCols = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CFARepeatPatternDim: Rows = %u, Cols = %u\n", + (unsigned) fCFARepeatPatternRows, + (unsigned) fCFARepeatPatternCols); + + } + + #endif + + break; + + } + + case tcCFAPattern: + { + + CheckCFA (parentCode, tagCode, fPhotometricInterpretation); + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + { + return false; + } + + if (!CheckTagCount (parentCode, tagCode, tagCount, fCFARepeatPatternRows * + fCFARepeatPatternCols)) + { + return false; + } + + if (fCFARepeatPatternRows < 1 || fCFARepeatPatternRows > kMaxCFAPattern || + fCFARepeatPatternCols < 1 || fCFARepeatPatternCols > kMaxCFAPattern) + { + return false; + } + + // Note that the Exif spec stores this array in a different + // scan order than the TIFF-EP spec. + + for (j = 0; j < fCFARepeatPatternRows; j++) + for (k = 0; k < fCFARepeatPatternCols; k++) + { + + fCFAPattern [j] [k] = stream.Get_uint8 (); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CFAPattern:\n"); + + for (j = 0; j < fCFARepeatPatternRows; j++) + { + + int32 spaces = 4; + + for (k = 0; k < fCFARepeatPatternCols; k++) + { + + while (spaces-- > 0) + { + printf (" "); + } + + const char *name = LookupCFAColor (fCFAPattern [j] [k]); + + spaces = 9 - (int32) strlen (name); + + printf ("%s", name); + + } + + printf ("\n"); + + } + + } + + #endif + + break; + + } + + case tcCFAPlaneColor: + { + + CheckCFA (parentCode, tagCode, fPhotometricInterpretation); + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + { + return false; + } + + if (!CheckTagCount (parentCode, tagCode, tagCount, 3, kMaxColorPlanes)) + { + return false; + } + + for (j = 0; j < kMaxColorPlanes; j++) + { + + if (j < tagCount) + fCFAPlaneColor [j] = stream.Get_uint8 (); + + else + fCFAPlaneColor [j] = 255; + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CFAPlaneColor:"); + + for (j = 0; j < tagCount; j++) + { + + printf (" %s", LookupCFAColor (fCFAPlaneColor [j])); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcCFALayout: + { + + CheckCFA (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCFALayout = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CFALayout: %s\n", + LookupCFALayout (fCFALayout)); + + } + + #endif + + break; + + } + + case tcLinearizationTable: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + fLinearizationTableType = tagType; + fLinearizationTableCount = tagCount; + fLinearizationTableOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + DumpTagValues (stream, + "Table", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcBlackLevelRepeatDim: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + { + return false; + } + + fBlackLevelRepeatRows = stream.TagValue_uint32 (tagType); + fBlackLevelRepeatCols = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BlackLevelRepeatDim: Rows = %u, Cols = %u\n", + (unsigned) fBlackLevelRepeatRows, + (unsigned) fBlackLevelRepeatCols); + + } + + #endif + + break; + + } + + case tcBlackLevel: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, fBlackLevelRepeatRows * + fBlackLevelRepeatCols * + fSamplesPerPixel)) + { + return false; + } + + if (fBlackLevelRepeatRows < 1 || fBlackLevelRepeatRows > kMaxBlackPattern || + fBlackLevelRepeatCols < 1 || fBlackLevelRepeatCols > kMaxBlackPattern || + fSamplesPerPixel < 1 || fSamplesPerPixel > kMaxSamplesPerPixel) + { + return false; + } + + for (j = 0; j < fBlackLevelRepeatRows; j++) + for (k = 0; k < fBlackLevelRepeatCols; k++) + for (n = 0; n < fSamplesPerPixel; n++) + { + + fBlackLevel [j] [k] [n] = stream.TagValue_real64 (tagType); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BlackLevel:"); + + if (fBlackLevelRepeatRows == 1 && + fBlackLevelRepeatCols == 1) + { + + for (n = 0; n < fSamplesPerPixel; n++) + { + printf (" %0.2f", fBlackLevel [0] [0] [n]); + } + + printf ("\n"); + + } + + else + { + + printf ("\n"); + + for (n = 0; n < fSamplesPerPixel; n++) + { + + if (fSamplesPerPixel > 1) + { + printf (" Sample: %u\n", (unsigned) n); + } + + for (j = 0; j < fBlackLevelRepeatRows; j++) + { + + printf (" "); + + for (k = 0; k < fBlackLevelRepeatCols; k++) + { + + printf (" %8.2f", fBlackLevel [j] [k] [n]); + + } + + printf ("\n"); + + } + + } + + } + + } + + #endif + + break; + + } + + case tcBlackLevelDeltaH: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + fBlackLevelDeltaHType = tagType; + fBlackLevelDeltaHCount = tagCount; + fBlackLevelDeltaHOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + DumpTagValues (stream, + "Delta", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcBlackLevelDeltaV: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + fBlackLevelDeltaVType = tagType; + fBlackLevelDeltaVCount = tagCount; + fBlackLevelDeltaVOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + DumpTagValues (stream, + "Delta", + parentCode, + tagCode, + tagType, + tagCount); + + } + + #endif + + break; + + } + + case tcWhiteLevel: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, fSamplesPerPixel)) + return false; + + for (j = 0; j < tagCount && j < kMaxSamplesPerPixel; j++) + { + + fWhiteLevel [j] = stream.TagValue_real64 (tagType); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("WhiteLevel:"); + + for (j = 0; j < tagCount && j < kMaxSamplesPerPixel; j++) + { + + printf (" %0.0f", fWhiteLevel [j]); + + } + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcDefaultScale: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fDefaultScaleH = stream.TagValue_urational (tagType); + fDefaultScaleV = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DefaultScale: H = %0.4f V = %0.4f\n", + fDefaultScaleH.As_real64 (), + fDefaultScaleV.As_real64 ()); + + } + + #endif + + break; + + } + + case tcDefaultCropOrigin: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fDefaultCropOriginH = stream.TagValue_urational (tagType); + fDefaultCropOriginV = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DefaultCropOrigin: H = %0.2f V = %0.2f\n", + fDefaultCropOriginH.As_real64 (), + fDefaultCropOriginV.As_real64 ()); + + } + + #endif + + break; + + } + + case tcDefaultCropSize: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fDefaultCropSizeH = stream.TagValue_urational (tagType); + fDefaultCropSizeV = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DefaultCropSize: H = %0.2f V = %0.2f\n", + fDefaultCropSizeH.As_real64 (), + fDefaultCropSizeV.As_real64 ()); + + } + + #endif + + break; + + } + + case tcDefaultUserCrop: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 4)) + return false; + + fDefaultUserCropT = stream.TagValue_urational (tagType); + fDefaultUserCropL = stream.TagValue_urational (tagType); + fDefaultUserCropB = stream.TagValue_urational (tagType); + fDefaultUserCropR = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DefaultUserCrop: T = %0.2lf L = %0.2lf B = %0.2lf R = %0.2lf\n", + (double) fDefaultUserCropT.As_real64 (), + (double) fDefaultUserCropL.As_real64 (), + (double) fDefaultUserCropB.As_real64 (), + (double) fDefaultUserCropR.As_real64 ()); + + + } + + #endif // qDNGValidate + + break; + + } + + case tcBayerGreenSplit: + { + + CheckCFA (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBayerGreenSplit = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("BayerGreenSplit: %u\n", (unsigned) fBayerGreenSplit); + } + + #endif + + break; + + } + + case tcChromaBlurRadius: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fChromaBlurRadius = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ChromaBlurRadius: %0.2f\n", + fChromaBlurRadius.As_real64 ()); + + } + + #endif + + break; + + } + + case tcAntiAliasStrength: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fAntiAliasStrength = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AntiAliasStrength: %0.2f\n", + fAntiAliasStrength.As_real64 ()); + + } + + #endif + + break; + + } + + case tcBestQualityScale: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBestQualityScale = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BestQualityScale: %0.4f\n", + fBestQualityScale.As_real64 ()); + + } + + #endif + + break; + + } + + case tcActiveArea: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 4)) + return false; + + fActiveArea.t = stream.TagValue_int32 (tagType); + fActiveArea.l = stream.TagValue_int32 (tagType); + fActiveArea.b = stream.TagValue_int32 (tagType); + fActiveArea.r = stream.TagValue_int32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ActiveArea: T = %d L = %d B = %d R = %d\n", + (int) fActiveArea.t, + (int) fActiveArea.l, + (int) fActiveArea.b, + (int) fActiveArea.r); + + } + + #endif + + break; + + } + + case tcMaskedAreas: + { + + CheckMainIFD (parentCode, tagCode, fNewSubFileType); + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + uint32 rect_count = tagCount / 4; + + if (!CheckTagCount (parentCode, tagCode, tagCount, rect_count * 4)) + return false; + + fMaskedAreaCount = rect_count; + + if (fMaskedAreaCount > kMaxMaskedAreas) + fMaskedAreaCount = kMaxMaskedAreas; + + for (j = 0; j < fMaskedAreaCount; j++) + { + + fMaskedArea [j].t = stream.TagValue_int32 (tagType); + fMaskedArea [j].l = stream.TagValue_int32 (tagType); + fMaskedArea [j].b = stream.TagValue_int32 (tagType); + fMaskedArea [j].r = stream.TagValue_int32 (tagType); + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("MaskedAreas: %u\n", (unsigned) fMaskedAreaCount); + + for (j = 0; j < fMaskedAreaCount; j++) + { + + printf (" Area [%u]: T = %d L = %d B = %d R = %d\n", + (unsigned) j, + (int) fMaskedArea [j].t, + (int) fMaskedArea [j].l, + (int) fMaskedArea [j].b, + (int) fMaskedArea [j].r); + + } + + } + + #endif + + break; + + } + + case tcPreviewApplicationName: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fPreviewInfo.fApplicationName, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PreviewApplicationName: "); + + DumpString (fPreviewInfo.fApplicationName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcPreviewApplicationVersion: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fPreviewInfo.fApplicationVersion, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PreviewApplicationVersion: "); + + DumpString (fPreviewInfo.fApplicationVersion); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcPreviewSettingsName: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fPreviewInfo.fSettingsName, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PreviewSettingsName: "); + + DumpString (fPreviewInfo.fSettingsName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcPreviewSettingsDigest: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) + return false; + + stream.Get (fPreviewInfo.fSettingsDigest.data, 16); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PreviewSettingsDigest: "); + + DumpFingerprint (fPreviewInfo.fSettingsDigest); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcPreviewColorSpace: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fPreviewInfo.fColorSpace = (PreviewColorSpaceEnum) + stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PreviewColorSpace: %s\n", + LookupPreviewColorSpace ((uint32) fPreviewInfo.fColorSpace)); + + } + + #endif + + break; + + } + + case tcPreviewDateTime: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fPreviewInfo.fDateTime, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("PreviewDateTime: "); + + DumpString (fPreviewInfo.fDateTime); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcRowInterleaveFactor: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 1)) + return false; + + fRowInterleaveFactor = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("RowInterleaveFactor: %u\n", + (unsigned) fRowInterleaveFactor); + + } + + #endif + + break; + + } + + case tcSubTileBlockSize: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fSubTileBlockRows = stream.TagValue_uint32 (tagType); + fSubTileBlockCols = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("SubTileBlockSize: rows = %u, cols = %u\n", + (unsigned) fSubTileBlockRows, + (unsigned) fSubTileBlockCols); + + } + + #endif + + break; + + } + + case tcOpcodeList1: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fOpcodeList1Count = tagCount; + fOpcodeList1Offset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OpcodeList1: count = %u, offset = %u\n", + (unsigned) fOpcodeList1Count, + (unsigned) fOpcodeList1Offset); + + } + + #endif + + break; + + } + + case tcOpcodeList2: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fOpcodeList2Count = tagCount; + fOpcodeList2Offset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OpcodeList2: count = %u, offset = %u\n", + (unsigned) fOpcodeList2Count, + (unsigned) fOpcodeList2Offset); + + } + + #endif + + break; + + } + + case tcOpcodeList3: + { + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fOpcodeList3Count = tagCount; + fOpcodeList3Offset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OpcodeList3: count = %u, offset = %u\n", + (unsigned) fOpcodeList3Count, + (unsigned) fOpcodeList3Offset); + + } + + #endif + + break; + + } + + case tcRawToPreviewGain: + { + + #if qDNGValidate + + if (fNewSubFileType != sfPreviewImage) + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed IFDs with NewSubFileType != PreviewImage", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttDouble); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 1)) + return false; + + fPreviewInfo.fRawToPreviewGain = stream.TagValue_real64 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("RawToPreviewGain = %f\n", + fPreviewInfo.fRawToPreviewGain); + + } + + #endif + + break; + + } + + case tcCacheVersion: + { + + #if qDNGValidate + + if (fNewSubFileType != sfPreviewImage) + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed IFDs with NewSubFileType != PreviewImage", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + CheckRawIFD (parentCode, tagCode, fPhotometricInterpretation); + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 1)) + return false; + + fPreviewInfo.fCacheVersion = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CacheVersion = 0x%x\n", + (unsigned) fPreviewInfo.fCacheVersion); + + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_ifd::PostParse () + { + + uint32 j; + uint32 k; + + // There is only one PlanarConfiguration for single sample imaages. + + if (fSamplesPerPixel == 1) + { + fPlanarConfiguration = pcInterleaved; + } + + // Default tile size. + + if (fTileWidth == 0) + { + fTileWidth = fImageWidth; + } + + if (fTileLength == 0) + { + fTileLength = fImageLength; + } + + // Default ActiveArea. + + dng_rect imageArea (0, 0, fImageLength, fImageWidth); + + if (fActiveArea.IsZero ()) + { + fActiveArea = imageArea; + } + + // Default crop size. + + if (fDefaultCropSizeH.d == 0) + { + fDefaultCropSizeH = dng_urational (fActiveArea.W (), 1); + } + + if (fDefaultCropSizeV.d == 0) + { + fDefaultCropSizeV = dng_urational (fActiveArea.H (), 1); + } + + // Default white level. + + uint32 defaultWhite = (fSampleFormat [0] == sfFloatingPoint) ? + 1 : + (uint32) ((((uint64) 1) << fBitsPerSample [0]) - 1); + + for (j = 0; j < kMaxSamplesPerPixel; j++) + { + + if (fWhiteLevel [j] < 0.0) + { + fWhiteLevel [j] = (real64) defaultWhite; + } + + } + + // Check AntiAliasStrength. + + if (fAntiAliasStrength.As_real64 () < 0.0 || + fAntiAliasStrength.As_real64 () > 1.0) + { + + #if qDNGValidate + + ReportWarning ("Invalid AntiAliasStrength"); + + #endif + + fAntiAliasStrength = dng_urational (1, 1); + + } + + // Check MaskedAreas. + + for (j = 0; j < fMaskedAreaCount; j++) + { + + const dng_rect &r = fMaskedArea [j]; + + if (r.IsEmpty () || ((r & imageArea) != r)) + { + + #if qDNGValidate + + ReportWarning ("Invalid MaskedArea"); + + #endif + + fMaskedAreaCount = 0; + + break; + + } + + if ((r & fActiveArea).NotEmpty ()) + { + + #if qDNGValidate + + ReportWarning ("MaskedArea overlaps ActiveArea"); + + #endif + + fMaskedAreaCount = 0; + + break; + + } + + for (k = 0; k < j; k++) + { + + if ((r & fMaskedArea [k]).NotEmpty ()) + { + + #if qDNGValidate + + ReportWarning ("MaskedAreas overlap each other"); + + #endif + + fMaskedAreaCount = 0; + + break; + + } + + } + + } + + } + +/*****************************************************************************/ + +bool dng_ifd::IsValidCFA (dng_shared &shared, + uint32 parentCode) + { + + uint32 j; + uint32 k; + uint32 n; + + #if !qDNGValidate + + (void) parentCode; // Unused + + #endif + + if (fCFARepeatPatternRows < 1 || fCFARepeatPatternRows > kMaxCFAPattern || + fCFARepeatPatternCols < 1 || fCFARepeatPatternCols > kMaxCFAPattern) + { + + #if qDNGValidate + + ReportError ("Missing or invalid CFAPatternRepeatDim", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + uint32 count [kMaxColorPlanes]; + + for (n = 0; n < shared.fCameraProfile.fColorPlanes; n++) + { + count [n] = 0; + } + + for (j = 0; j < fCFARepeatPatternRows; j++) + { + + for (k = 0; k < fCFARepeatPatternCols; k++) + { + + bool found = false; + + for (n = 0; n < shared.fCameraProfile.fColorPlanes; n++) + { + + if (fCFAPattern [j] [k] == fCFAPlaneColor [n]) + { + found = true; + count [n] ++; + break; + } + + } + + if (!found) + { + + #if qDNGValidate + + ReportError ("CFAPattern contains colors not included in the CFAPlaneColor tag", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + } + + for (n = 0; n < shared.fCameraProfile.fColorPlanes; n++) + { + + if (count [n] == 0) + { + + #if qDNGValidate + + ReportError ("CFAPattern does not contain all the colors in the CFAPlaneColor tag", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + if (fCFALayout < 1 || fCFALayout > 9) + { + + #if qDNGValidate + + ReportError ("Invalid CFALayout", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_ifd::IsValidDNG (dng_shared &shared, + uint32 parentCode) + { + + uint32 j; + + bool isFloatingPoint = (fSampleFormat [0] == sfFloatingPoint); + + dng_rect imageArea (0, 0, fImageLength, fImageWidth); + + uint32 defaultWhite = isFloatingPoint ? + 1 : + (uint32) ((((uint64) 1) << fBitsPerSample [0]) - 1); + + bool isMonochrome = (shared.fCameraProfile.fColorPlanes == 1); + bool isColor = !isMonochrome; + + bool isMainIFD = (fNewSubFileType == sfMainImage); + + // Check NewSubFileType. + + if (!fUsesNewSubFileType) + { + + #if qDNGValidate + + ReportError ("Missing NewSubFileType", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fNewSubFileType != sfMainImage && + fNewSubFileType != sfPreviewImage && + fNewSubFileType != sfTransparencyMask && + fNewSubFileType != sfPreviewMask && + fNewSubFileType != sfAltPreviewImage) + { + + #if qDNGValidate + + ReportError ("Unexpected NewSubFileType", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check ImageWidth and ImageLength. + + if (fImageWidth < 1) + { + + #if qDNGValidate + + ReportError ("Missing or invalid ImageWidth", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fImageLength < 1) + { + + #if qDNGValidate + + ReportError ("Missing or invalid ImageLength", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fImageWidth > kMaxImageSide || + fImageLength > kMaxImageSide) + { + + #if qDNGValidate + + ReportWarning ("Image size is larger than supported"); + + #endif + + return false; + + } + + // Check PhotometricInterpretation. + + if (fNewSubFileType == sfTransparencyMask || + fNewSubFileType == sfPreviewMask) + { + + if (fPhotometricInterpretation != piTransparencyMask) + { + + #if qDNGValidate + + ReportError ("NewSubFileType requires PhotometricInterpretation = TransparencyMask", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + else + { + + switch (fPhotometricInterpretation) + { + + case piBlackIsZero: + case piRGB: + case piYCbCr: + { + + if (isMainIFD) + { + + #if qDNGValidate + + ReportError ("PhotometricInterpretation requires NewSubFileType = 1", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + break; + + } + + case piCFA: + { + + if (!isMainIFD) + { + + #if qDNGValidate + + ReportError ("PhotometricInterpretation requires NewSubFileType = 0", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + break; + + } + + case piLinearRaw: + break; + + default: + { + + #if qDNGValidate + + ReportError ("Missing or invalid PhotometricInterpretation", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + } + + switch (fPhotometricInterpretation) + { + + case piBlackIsZero: + { + + // Allow black in white previews even in color images since the + // raw processing software may be converting to grayscale. + + if (isColor && isMainIFD) + { + + #if qDNGValidate + + ReportError ("PhotometricInterpretation forbids use of ColorMatrix1 tag", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + break; + + } + + case piRGB: + case piYCbCr: + { + + // Allow color previews even in monochrome DNG files, since the + // raw procesing software may be adding color effects. + + break; + + } + + case piCFA: + { + + if (isMonochrome) + { + + #if qDNGValidate + + ReportError ("PhotometricInterpretation requires use of ColorMatrix1 tag", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + break; + + } + + } + + if (isFloatingPoint) + { + + if (fPhotometricInterpretation != piCFA && + fPhotometricInterpretation != piLinearRaw && + fPhotometricInterpretation != piTransparencyMask) + { + + #if qDNGValidate + + ReportError ("Floating point data requires PhotometricInterpretation CFA or LinearRaw or TransparencyMask", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check SamplesPerPixel and BitsPerSample. + + uint32 minSamplesPerPixel = 1; + uint32 maxSamplesPerPixel = 1; + + uint32 minBitsPerSample = 8; + uint32 maxBitsPerSample = 16; + + switch (fPhotometricInterpretation) + { + + case piBlackIsZero: + break; + + case piRGB: + case piYCbCr: + { + minSamplesPerPixel = 3; + maxSamplesPerPixel = 3; + break; + } + + case piCFA: + { + maxSamplesPerPixel = kMaxSamplesPerPixel; + maxBitsPerSample = 32; + break; + } + + case piLinearRaw: + { + minSamplesPerPixel = shared.fCameraProfile.fColorPlanes; + maxSamplesPerPixel = shared.fCameraProfile.fColorPlanes; + maxBitsPerSample = 32; + break; + } + + case piTransparencyMask: + { + minBitsPerSample = 8; + maxBitsPerSample = 16; + break; + } + + } + + if (isFloatingPoint) + { + minBitsPerSample = 16; + maxBitsPerSample = 32; + } + + if (fSamplesPerPixel < minSamplesPerPixel || + fSamplesPerPixel > maxSamplesPerPixel) + { + + #if qDNGValidate + + ReportError ("Missing or invalid SamplesPerPixel", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + for (j = 0; j < kMaxSamplesPerPixel; j++) + { + + if (j < fSamplesPerPixel) + { + + if (fBitsPerSample [j] < minBitsPerSample || + fBitsPerSample [j] > maxBitsPerSample) + { + + #if qDNGValidate + + ReportError ("Missing or invalid BitsPerSample", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (isFloatingPoint && + fBitsPerSample [j] != 16 && + fBitsPerSample [j] != 24 && + fBitsPerSample [j] != 32) + { + + #if qDNGValidate + + ReportError ("Invalid BitsPerSample for floating point", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (minBitsPerSample == 8 && + maxBitsPerSample == 16 && + fBitsPerSample [j] != 8 && + fBitsPerSample [j] != 16) + { + + #if qDNGValidate + + ReportError ("Rendered previews and integer masks require 8 or 16 bits per sample", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (j > 0 && fBitsPerSample [j] != fBitsPerSample [0]) + { + + #if qDNGValidate + + ReportError ("BitsPerSample not equal for all samples", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + else + { + + if (fBitsPerSample [j] != 0) + { + + #if qDNGValidate + + ReportError ("Too many values specified in BitsPerSample", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + } + + // Check Compression. + + switch (fCompression) + { + + case ccUncompressed: + break; + +#if GPR_WRITING || GPR_READING + case ccVc5: + break; +#endif + + case ccJPEG: + { + + if (fPhotometricInterpretation == piRGB) + { + + #if qDNGValidate + + ReportError ("JPEG previews should use PhotometricInterpretation = YCbYb", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fBitsPerSample [0] > 16) + { + + #if qDNGValidate + + ReportError ("JPEG compression is limited to 16 bits/sample", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + break; + + } + + case ccLossyJPEG: + { + + if (fPhotometricInterpretation != piLinearRaw) + { + + #if qDNGValidate + + ReportError ("Lossy JPEG compression code requires PhotometricInterpretation = LinearRaw", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fBitsPerSample [0] != 8) + { + + #if qDNGValidate + + ReportError ("Lossy JPEG compression is limited to 8 bits/sample", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + break; + + } + + case ccDeflate: + { + + if (!isFloatingPoint && + fBitsPerSample [0] != 32 && + fPhotometricInterpretation != piTransparencyMask) + { + + #if qDNGValidate + + ReportError ("ZIP compression is limited to floating point and 32-bit integer and transparency masks", + LookupParentCode (parentCode)); + + #endif + + } + + break; + + } + + default: + { + + #if qDNGValidate + + ReportError ("Unsupported Compression", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check Predictor. + + if (isFloatingPoint && fCompression == ccDeflate && + (fPredictor == cpFloatingPoint || + fPredictor == cpFloatingPointX2 || + fPredictor == cpFloatingPointX4)) + { + + // These combinations are supported. + + } + + else if (!isFloatingPoint && fCompression == ccDeflate && + (fPredictor == cpHorizontalDifference || + fPredictor == cpHorizontalDifferenceX2 || + fPredictor == cpHorizontalDifferenceX4)) + { + + // These combinations are supported. + + } + + else if (fPredictor != cpNullPredictor) + { + + #if qDNGValidate + + ReportError ("Unsupported Predictor", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check FillOrder. + + if (fFillOrder != 1) + { + + #if qDNGValidate + + ReportError ("Unsupported FillOrder", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check PlanarConfiguration. + + if (fPlanarConfiguration != pcInterleaved) + { + + #if qDNGValidate + + ReportError ("Unsupported PlanarConfiguration", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check ExtraSamples. + + if (fExtraSamplesCount != 0) + { + + #if qDNGValidate + + ReportError ("Unsupported ExtraSamples", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check SampleFormat. + + for (j = 0; j < fSamplesPerPixel; j++) + { + + if (fSampleFormat [j] != (isFloatingPoint ? sfFloatingPoint : sfUnsignedInteger)) + { + + #if qDNGValidate + + ReportError ("Unsupported SampleFormat", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check Orientation. + + if (fOrientation > 9) + { + + #if qDNGValidate + + ReportError ("Unknown Orientation", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + #if qDNGValidate + + if (fOrientation != 0 && parentCode != 0) + { + + ReportWarning ("Unexpected Orientation tag", + LookupParentCode (parentCode)); + + } + + if (fOrientation == 0 && parentCode == 0) + { + + ReportWarning ("Missing Orientation tag", + LookupParentCode (parentCode)); + + } + + #endif + + // Check Strips vs. Tiles. + + if (!fUsesStrips && !fUsesTiles) + { + + #if qDNGValidate + + ReportError ("IFD uses neither strips nor tiles", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fUsesStrips && fUsesTiles) + { + + #if qDNGValidate + + ReportError ("IFD uses both strips and tiles", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check tile info. + + uint32 tilesWide = (fImageWidth + fTileWidth - 1) / fTileWidth; + uint32 tilesHigh = (fImageLength + fTileLength - 1) / fTileLength; + + uint32 tileCount = tilesWide * tilesHigh; + + if (fTileOffsetsCount != tileCount) + { + + #if qDNGValidate + + ReportError ("Missing or invalid Strip/TileOffsets", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fTileByteCountsCount != tileCount) + { + + #if qDNGValidate + + ReportError ("Missing or invalid Strip/TileByteCounts", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check CFA pattern. + + if (fPhotometricInterpretation == piCFA) + { + + if (!IsValidCFA (shared, parentCode)) + { + + return false; + + } + + } + + // Check ActiveArea. + + if (((fActiveArea & imageArea) != fActiveArea) || fActiveArea.IsEmpty ()) + { + + #if qDNGValidate + + ReportError ("Invalid ActiveArea", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fActiveArea != imageArea) + { + + if (shared.fDNGBackwardVersion < dngVersion_1_1_0_0) + { + + #if qDNGValidate + + ReportError ("Non-default ActiveArea tag not allowed in this DNG version", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check LinearizationTable. + + if (fLinearizationTableCount) + { + + if (fLinearizationTableType != ttShort) + { + + #if qDNGValidate + + ReportError ("Invalidate LinearizationTable type", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (fLinearizationTableCount < 2 || + fLinearizationTableCount > 65536) + { + + #if qDNGValidate + + ReportError ("Invalidate LinearizationTable count", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (isFloatingPoint || fBitsPerSample [0] > 16) + { + + #if qDNGValidate + + ReportError ("Linearization table not allowed for this data type", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check BlackLevelRepeatDim. + + if (fBlackLevelRepeatRows < 1 || fBlackLevelRepeatRows > kMaxBlackPattern || + fBlackLevelRepeatCols < 1 || fBlackLevelRepeatCols > kMaxBlackPattern) + { + + #if qDNGValidate + + ReportError ("Invalid BlackLevelRepeatDim", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check BlackLevelDeltaH. + + if (fBlackLevelDeltaHCount != 0 && + fBlackLevelDeltaHCount != fActiveArea.W ()) + { + + #if qDNGValidate + + ReportError ("Invalid BlackLevelDeltaH count", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check BlackLevelDeltaV. + + if (fBlackLevelDeltaVCount != 0 && + fBlackLevelDeltaVCount != fActiveArea.H ()) + { + + #if qDNGValidate + + ReportError ("Invalid BlackLevelDeltaV count", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + // Check WhiteLevel. + + real64 maxWhite = fLinearizationTableCount ? 65535.0 + : (real64) defaultWhite; + + for (j = 0; j < fSamplesPerPixel; j++) + { + + if (fWhiteLevel [j] < 1.0 || (fWhiteLevel [j] > maxWhite && !isFloatingPoint)) + { + + #if qDNGValidate + + ReportError ("Invalid WhiteLevel", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check BlackLevel. + + for (j = 0; j < kMaxBlackPattern; j++) + { + + for (uint32 k = 0; k < kMaxBlackPattern; k++) + { + + for (uint32 s = 0; s < kMaxSamplesPerPixel; s++) + { + + const real64 black = fBlackLevel [j][k][s]; + + if (black >= fWhiteLevel [s]) + { + + #if qDNGValidate + + ReportError ("Invalid BlackLevel", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + } + + } + + // Check DefaultScale. + + if (fDefaultScaleH.As_real64 () <= 0.0 || + fDefaultScaleV.As_real64 () <= 0.0) + { + + #if qDNGValidate + + ReportError ("Invalid DefaultScale"); + + #endif + + return false; + + } + + // Check BestQualityScale. + + if (fBestQualityScale.As_real64 () < 1.0) + { + + #if qDNGValidate + + ReportError ("Invalid BestQualityScale"); + + #endif + + return false; + + } + + // Check DefaultCropOrigin. + + if (fDefaultCropOriginH.As_real64 () < 0.0 || + fDefaultCropOriginV.As_real64 () < 0.0 || + fDefaultCropOriginH.As_real64 () >= (real64) fActiveArea.W () || + fDefaultCropOriginV.As_real64 () >= (real64) fActiveArea.H ()) + { + + #if qDNGValidate + + ReportError ("Invalid DefaultCropOrigin"); + + #endif + + return false; + + } + + // Check DefaultCropSize. + + if (fDefaultCropSizeH.As_real64 () <= 0.0 || + fDefaultCropSizeV.As_real64 () <= 0.0 || + fDefaultCropSizeH.As_real64 () > (real64) fActiveArea.W () || + fDefaultCropSizeV.As_real64 () > (real64) fActiveArea.H ()) + { + + #if qDNGValidate + + ReportError ("Invalid DefaultCropSize"); + + #endif + + return false; + + } + + // Check DefaultCrop area. + + if (fDefaultCropOriginH.As_real64 () + + fDefaultCropSizeH .As_real64 () > (real64) fActiveArea.W () || + fDefaultCropOriginV.As_real64 () + + fDefaultCropSizeV .As_real64 () > (real64) fActiveArea.H ()) + { + + #if qDNGValidate + + ReportError ("Default crop extends outside ActiveArea"); + + #endif + + return false; + + } + + // Check DefaultUserCrop. + + if (fDefaultUserCropT.As_real64 () < 0.0 || + fDefaultUserCropL.As_real64 () < 0.0 || + fDefaultUserCropB.As_real64 () > 1.0 || + fDefaultUserCropR.As_real64 () > 1.0 || + fDefaultUserCropT.As_real64 () >= fDefaultUserCropB.As_real64 () || + fDefaultUserCropL.As_real64 () >= fDefaultUserCropR.As_real64 ()) + { + + #if qDNGValidate + + ReportError ("Invalid DefaultUserCrop"); + + #endif // qDNGValidate + + return false; + + } + + // The default crop and default user crop tags are not allowed for the + // non-main image. If they are there, at least require that they be NOPs. + + if (!isMainIFD) + { + + if (Round_int32 (fDefaultCropOriginH.As_real64 ()) != 0 || + Round_int32 (fDefaultCropOriginV.As_real64 ()) != 0) + { + + #if qDNGValidate + + ReportError ("non-default DefaultCropOrigin on non-main image"); + + #endif + + return false; + + } + + if (Round_int32 (fDefaultCropSizeH.As_real64 ()) != (int32) fImageWidth || + Round_int32 (fDefaultCropSizeV.As_real64 ()) != (int32) fImageLength) + { + + #if qDNGValidate + + ReportError ("non-default DefaultCropSize on non-main image"); + + #endif + + return false; + + } + + if (fDefaultUserCropT.As_real64 () != 0.0 || + fDefaultUserCropL.As_real64 () != 0.0 || + fDefaultUserCropB.As_real64 () != 1.0 || + fDefaultUserCropR.As_real64 () != 1.0) + { + + #if qDNGValidate + + ReportError ("non-default DefaultCUserCrop on non-main image"); + + #endif // qDNGValidate + + return false; + + } + + } + + // Warning if too little padding on CFA image. + + #if qDNGValidate + + if (fPhotometricInterpretation == piCFA) + { + + const real64 kMinPad = 1.9; + + if (fDefaultCropOriginH.As_real64 () < kMinPad) + { + + ReportWarning ("Too little padding on left edge of CFA image", + "possible interpolation artifacts"); + + } + + if (fDefaultCropOriginV.As_real64 () < kMinPad) + { + + ReportWarning ("Too little padding on top edge of CFA image", + "possible interpolation artifacts"); + + } + + if (fDefaultCropOriginH.As_real64 () + + fDefaultCropSizeH .As_real64 () > (real64) fActiveArea.W () - kMinPad) + { + + ReportWarning ("Too little padding on right edge of CFA image", + "possible interpolation artifacts"); + + } + + if (fDefaultCropOriginV.As_real64 () + + fDefaultCropSizeV .As_real64 () > (real64) fActiveArea.H () - kMinPad) + { + + ReportWarning ("Too little padding on bottom edge of CFA image", + "possible interpolation artifacts"); + + } + + } + + #endif + + // Check RowInterleaveFactor + + if (fRowInterleaveFactor != 1) + { + + if (fRowInterleaveFactor < 1 || + fRowInterleaveFactor > fImageLength) + { + + #if qDNGValidate + + ReportError ("RowInterleaveFactor out of valid range", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (shared.fDNGBackwardVersion < dngVersion_1_2_0_0) + { + + #if qDNGValidate + + ReportError ("Non-default RowInterleaveFactor tag not allowed in this DNG version", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + // Check SubTileBlockSize + + if (fSubTileBlockRows != 1 || fSubTileBlockCols != 1) + { + + if (fSubTileBlockRows < 2 || fSubTileBlockRows > fTileLength || + fSubTileBlockCols < 1 || fSubTileBlockCols > fTileWidth) + { + + #if qDNGValidate + + ReportError ("SubTileBlockSize out of valid range", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if ((fTileLength % fSubTileBlockRows) != 0 || + (fTileWidth % fSubTileBlockCols) != 0) + { + + #if qDNGValidate + + ReportError ("TileSize not exact multiple of SubTileBlockSize", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + if (shared.fDNGBackwardVersion < dngVersion_1_2_0_0) + { + + #if qDNGValidate + + ReportError ("Non-default SubTileBlockSize tag not allowed in this DNG version", + LookupParentCode (parentCode)); + + #endif + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +uint32 dng_ifd::TilesAcross () const + { + + if (fTileWidth) + { + + return (fImageWidth + fTileWidth - 1) / fTileWidth; + + } + + return 0; + + } + +/*****************************************************************************/ + +uint32 dng_ifd::TilesDown () const + { + + if (fTileLength) + { + + return (fImageLength + fTileLength - 1) / fTileLength; + + } + + return 0; + + } + +/*****************************************************************************/ + +uint32 dng_ifd::TilesPerImage () const + { + + uint32 total = TilesAcross () * TilesDown (); + + if (fPlanarConfiguration == pcPlanar) + { + + total *= fSamplesPerPixel; + + } + + return total; + + } + +/*****************************************************************************/ + +dng_rect dng_ifd::TileArea (uint32 rowIndex, + uint32 colIndex) const + { + + dng_rect r; + + r.t = rowIndex * fTileLength; + r.b = r.t + fTileLength; + + r.l = colIndex * fTileWidth; + r.r = r.l + fTileWidth; + + // If this IFD is using strips rather than tiles, the last strip + // is trimmed so it does not extend beyond the end of the image. + + if (fUsesStrips) + { + + r.b = Min_uint32 (r.b, fImageLength); + + } + + return r; + + } + +/*****************************************************************************/ + +uint32 dng_ifd::TileByteCount (const dng_rect &tile) const + { + + if ( fCompression == ccUncompressed ) + + { + + uint32 bitsPerRow = tile.W () * + fBitsPerSample [0]; + + if (fPlanarConfiguration == pcInterleaved) + { + + bitsPerRow *= fSamplesPerPixel; + + } + + uint32 bytesPerRow = (bitsPerRow + 7) >> 3; + + if (fPlanarConfiguration == pcRowInterleaved) + { + + bytesPerRow *= fSamplesPerPixel; + + } + + return bytesPerRow * tile.H (); + + } + + return 0; + + } + +/*****************************************************************************/ + +void dng_ifd::SetSingleStrip () + { + + fTileWidth = fImageWidth; + fTileLength = fImageLength; + + fUsesTiles = false; + fUsesStrips = true; + + } + +/*****************************************************************************/ + +void dng_ifd::FindTileSize (uint32 bytesPerTile, + uint32 cellH, + uint32 cellV) + { + + uint32 bytesPerSample = fSamplesPerPixel * + ((fBitsPerSample [0] + 7) >> 3); + + uint32 samplesPerTile = bytesPerTile / bytesPerSample; + + uint32 tileSide = Round_uint32 (sqrt ((real64) samplesPerTile)); + + fTileWidth = Min_uint32 (fImageWidth, tileSide); + + uint32 across = TilesAcross (); + + fTileWidth = (fImageWidth + across - 1) / across; + + fTileWidth = ((fTileWidth + cellH - 1) / cellH) * cellH; + + fTileLength = Pin_uint32 (1, + samplesPerTile / fTileWidth, + fImageLength); + + uint32 down = TilesDown (); + + fTileLength = (fImageLength + down - 1) / down; + + fTileLength = ((fTileLength + cellV - 1) / cellV) * cellV; + + fUsesTiles = true; + fUsesStrips = false; + + } + +/*****************************************************************************/ + +void dng_ifd::FindStripSize (uint32 bytesPerStrip, + uint32 cellV) + { + + uint32 bytesPerSample = fSamplesPerPixel * + ((fBitsPerSample [0] + 7) >> 3); + + uint32 samplesPerStrip = bytesPerStrip / bytesPerSample; + + fTileWidth = fImageWidth; + + fTileLength = Pin_uint32 (1, + samplesPerStrip / fTileWidth, + fImageLength); + + uint32 down = TilesDown (); + + fTileLength = (fImageLength + down - 1) / down; + + fTileLength = ((fTileLength + cellV - 1) / cellV) * cellV; + + fUsesTiles = false; + fUsesStrips = true; + + } + +/*****************************************************************************/ + +uint32 dng_ifd::PixelType () const + { + + if (fSampleFormat [0] == sfFloatingPoint) + { + return ttFloat; + } + + if (fBitsPerSample [0] <= 8) + { + return ttByte; + } + + else if (fBitsPerSample [0] <= 16) + { + return ttShort; + } + + return ttLong; + + } + +/*****************************************************************************/ + +bool dng_ifd::IsBaselineJPEG () const + { + + if (fBitsPerSample [0] != 8) + { + return false; + } + + if (fSampleFormat [0] != sfUnsignedInteger) + { + return false; + } + + if (fCompression == ccLossyJPEG) + { + return true; + } + + if (fCompression != ccJPEG) + { + return false; + } + + switch (fPhotometricInterpretation) + { + + case piBlackIsZero: + { + return (fSamplesPerPixel == 1); + } + + case piYCbCr: + { + return (fSamplesPerPixel == 3 ) && + (fPlanarConfiguration == pcInterleaved); + } + + default: + break; + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_ifd::CanRead () const + { + + dng_read_image reader; + + return reader.CanRead (*this); + + } + +/*****************************************************************************/ + +void dng_ifd::ReadImage (dng_host &host, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage, + dng_fingerprint *jpegDigest) const + { + + dng_read_image reader; + + reader.Read (host, + *this, + stream, + image, + jpegImage, + jpegDigest); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_ifd.h b/source/lib/dng_sdk/dng_ifd.h new file mode 100644 index 0000000..c0b681d --- /dev/null +++ b/source/lib/dng_sdk/dng_ifd.h @@ -0,0 +1,305 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_ifd.h#3 $ */ +/* $DateTime: 2012/06/05 11:05:39 $ */ +/* $Change: 833352 $ */ +/* $Author: tknoll $ */ + +/** \file + * DNG image file directory support. + */ + +/*****************************************************************************/ + +#ifndef __dng_ifd__ +#define __dng_ifd__ + +/*****************************************************************************/ + +#include "dng_fingerprint.h" +#include "dng_rect.h" +#include "dng_shared.h" +#include "dng_stream.h" +#include "dng_string.h" +#include "dng_sdk_limits.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +class dng_preview_info + { + + public: + + bool fIsPrimary; + + dng_string fApplicationName; + + dng_string fApplicationVersion; + + dng_string fSettingsName; + + dng_fingerprint fSettingsDigest; + + PreviewColorSpaceEnum fColorSpace; + + dng_string fDateTime; + + real64 fRawToPreviewGain; + + uint32 fCacheVersion; + + public: + + dng_preview_info (); + + ~dng_preview_info (); + + }; + +/*****************************************************************************/ + +/// \brief Container for a single image file directory of a digital negative. +/// +/// See \ref spec_dng "DNG 1.1.0 specification" for documentation of specific tags. + +class dng_ifd + { + + public: + + bool fUsesNewSubFileType; + + uint32 fNewSubFileType; + + uint32 fImageWidth; + uint32 fImageLength; + + uint32 fBitsPerSample [kMaxSamplesPerPixel]; + + uint32 fCompression; + + uint32 fPredictor; + + uint32 fPhotometricInterpretation; + + uint32 fFillOrder; + + uint32 fOrientation; + uint32 fOrientationType; + uint64 fOrientationOffset; + bool fOrientationBigEndian; + + uint32 fSamplesPerPixel; + + uint32 fPlanarConfiguration; + + real64 fXResolution; + real64 fYResolution; + + uint32 fResolutionUnit; + + bool fUsesStrips; + bool fUsesTiles; + + uint32 fTileWidth; + uint32 fTileLength; + + enum + { + kMaxTileInfo = 32 + }; + + uint32 fTileOffsetsType; + uint32 fTileOffsetsCount; + uint64 fTileOffsetsOffset; + uint64 fTileOffset [kMaxTileInfo]; + + uint32 fTileByteCountsType; + uint32 fTileByteCountsCount; + uint64 fTileByteCountsOffset; + uint32 fTileByteCount [kMaxTileInfo]; + + uint32 fSubIFDsCount; + uint64 fSubIFDsOffset; + + uint32 fExtraSamplesCount; + uint32 fExtraSamples [kMaxSamplesPerPixel]; + + uint32 fSampleFormat [kMaxSamplesPerPixel]; + + uint32 fJPEGTablesCount; + uint64 fJPEGTablesOffset; + + uint64 fJPEGInterchangeFormat; + uint32 fJPEGInterchangeFormatLength; + + real64 fYCbCrCoefficientR; + real64 fYCbCrCoefficientG; + real64 fYCbCrCoefficientB; + + uint32 fYCbCrSubSampleH; + uint32 fYCbCrSubSampleV; + + uint32 fYCbCrPositioning; + + real64 fReferenceBlackWhite [6]; + + uint32 fCFARepeatPatternRows; + uint32 fCFARepeatPatternCols; + + uint8 fCFAPattern [kMaxCFAPattern] [kMaxCFAPattern]; + + uint8 fCFAPlaneColor [kMaxColorPlanes]; + + uint32 fCFALayout; + + uint32 fLinearizationTableType; + uint32 fLinearizationTableCount; + uint64 fLinearizationTableOffset; + + uint32 fBlackLevelRepeatRows; + uint32 fBlackLevelRepeatCols; + + real64 fBlackLevel [kMaxBlackPattern] [kMaxBlackPattern] [kMaxSamplesPerPixel]; + + uint32 fBlackLevelDeltaHType; + uint32 fBlackLevelDeltaHCount; + uint64 fBlackLevelDeltaHOffset; + + uint32 fBlackLevelDeltaVType; + uint32 fBlackLevelDeltaVCount; + uint64 fBlackLevelDeltaVOffset; + + real64 fWhiteLevel [kMaxSamplesPerPixel]; + + dng_urational fDefaultScaleH; + dng_urational fDefaultScaleV; + + dng_urational fBestQualityScale; + + dng_urational fDefaultCropOriginH; + dng_urational fDefaultCropOriginV; + + dng_urational fDefaultCropSizeH; + dng_urational fDefaultCropSizeV; + + dng_urational fDefaultUserCropT; + dng_urational fDefaultUserCropL; + dng_urational fDefaultUserCropB; + dng_urational fDefaultUserCropR; + + uint32 fBayerGreenSplit; + + dng_urational fChromaBlurRadius; + + dng_urational fAntiAliasStrength; + + dng_rect fActiveArea; + + uint32 fMaskedAreaCount; + dng_rect fMaskedArea [kMaxMaskedAreas]; + + uint32 fRowInterleaveFactor; + + uint32 fSubTileBlockRows; + uint32 fSubTileBlockCols; + + dng_preview_info fPreviewInfo; + + uint32 fOpcodeList1Count; + uint64 fOpcodeList1Offset; + + uint32 fOpcodeList2Count; + uint64 fOpcodeList2Offset; + + uint32 fOpcodeList3Count; + uint64 fOpcodeList3Offset; + + bool fLosslessJPEGBug16; + + uint32 fSampleBitShift; + + uint64 fThisIFD; + uint64 fNextIFD; + + int32 fCompressionQuality; + + bool fPatchFirstJPEGByte; + + public: + + dng_ifd (); + + virtual ~dng_ifd (); + + virtual bool ParseTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual void PostParse (); + + virtual bool IsValidDNG (dng_shared &shared, + uint32 parentCode); + + dng_rect Bounds () const + { + return dng_rect (0, + 0, + fImageLength, + fImageWidth); + } + + uint32 TilesAcross () const; + + uint32 TilesDown () const; + + uint32 TilesPerImage () const; + + dng_rect TileArea (uint32 rowIndex, + uint32 colIndex) const; + + virtual uint32 TileByteCount (const dng_rect &tile) const; + + void SetSingleStrip (); + + void FindTileSize (uint32 bytesPerTile = 128 * 1024, + uint32 cellH = 16, + uint32 cellV = 16); + + void FindStripSize (uint32 bytesPerStrip = 128 * 1024, + uint32 cellV = 16); + + virtual uint32 PixelType () const; + + virtual bool IsBaselineJPEG () const; + + virtual bool CanRead () const; + + virtual void ReadImage (dng_host &host, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage = NULL, + dng_fingerprint *jpegDigest = NULL) const; + + protected: + + virtual bool IsValidCFA (dng_shared &shared, + uint32 parentCode); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_image.cpp b/source/lib/dng_sdk/dng_image.cpp new file mode 100644 index 0000000..50fd085 --- /dev/null +++ b/source/lib/dng_sdk/dng_image.cpp @@ -0,0 +1,848 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_image.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_image.h" + +#include "dng_assertions.h" +#include "dng_exceptions.h" +#include "dng_orientation.h" +#include "dng_pixel_buffer.h" +#include "dng_tag_types.h" +#include "dng_tile_iterator.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_tile_buffer::dng_tile_buffer (const dng_image &image, + const dng_rect &tile, + bool dirty) + + : fImage (image) + , fRefData (NULL) + + { + + fImage.AcquireTileBuffer (*this, + tile, + dirty); + + } + +/*****************************************************************************/ + +dng_tile_buffer::~dng_tile_buffer () + { + + fImage.ReleaseTileBuffer (*this); + + } + +/*****************************************************************************/ + +dng_const_tile_buffer::dng_const_tile_buffer (const dng_image &image, + const dng_rect &tile) + + : dng_tile_buffer (image, tile, false) + + { + + } + +/*****************************************************************************/ + +dng_const_tile_buffer::~dng_const_tile_buffer () + { + + } + +/*****************************************************************************/ + +dng_dirty_tile_buffer::dng_dirty_tile_buffer (dng_image &image, + const dng_rect &tile) + + : dng_tile_buffer (image, tile, true) + + { + + } + +/*****************************************************************************/ + +dng_dirty_tile_buffer::~dng_dirty_tile_buffer () + { + + } + +/*****************************************************************************/ + +dng_image::dng_image (const dng_rect &bounds, + uint32 planes, + uint32 pixelType) + + : fBounds (bounds) + , fPlanes (planes) + , fPixelType (pixelType) + + { + + if (bounds.IsEmpty () || planes == 0 || PixelSize () == 0) + { + + #if qDNGValidate + + ReportError ("Fuzz: Attempt to create zero size image"); + + #endif + + ThrowBadFormat (); + + } + + } + +/*****************************************************************************/ + +dng_image::~dng_image () + { + + } + +/*****************************************************************************/ + +dng_image * dng_image::Clone () const + { + + ThrowProgramError ("Clone is not supported by this dng_image subclass"); + + return NULL; + + } + +/*****************************************************************************/ + +void dng_image::SetPixelType (uint32 pixelType) + { + + if (TagTypeSize (pixelType) != PixelSize ()) + { + + ThrowProgramError ("Cannot change pixel size for existing image"); + + } + + fPixelType = pixelType; + + } + +/*****************************************************************************/ + +uint32 dng_image::PixelSize () const + { + + return TagTypeSize (PixelType ()); + + } + +/*****************************************************************************/ + +uint32 dng_image::PixelRange () const + { + + switch (fPixelType) + { + + case ttByte: + case ttSByte: + { + return 0x0FF; + } + + case ttShort: + case ttSShort: + { + return 0x0FFFF; + } + + case ttLong: + case ttSLong: + { + return 0xFFFFFFFF; + } + + default: + break; + + } + + return 0; + + } + +/*****************************************************************************/ + +dng_rect dng_image::RepeatingTile () const + { + + return fBounds; + + } + +/*****************************************************************************/ + +void dng_image::AcquireTileBuffer (dng_tile_buffer & /* buffer */, + const dng_rect & /* area */, + bool /* dirty */) const + { + + ThrowProgramError (); + + } + +/*****************************************************************************/ + +void dng_image::ReleaseTileBuffer (dng_tile_buffer & /* buffer */) const + { + + } + +/*****************************************************************************/ + +void dng_image::DoGet (dng_pixel_buffer &buffer) const + { + + dng_rect tile; + + dng_tile_iterator iter (*this, buffer.fArea); + + while (iter.GetOneTile (tile)) + { + + dng_const_tile_buffer tileBuffer (*this, tile); + + buffer.CopyArea (tileBuffer, + tile, + buffer.fPlane, + buffer.fPlanes); + + } + + } + +/*****************************************************************************/ + +void dng_image::DoPut (const dng_pixel_buffer &buffer) + { + + dng_rect tile; + + dng_tile_iterator iter (*this, buffer.fArea); + + while (iter.GetOneTile (tile)) + { + + dng_dirty_tile_buffer tileBuffer (*this, tile); + + tileBuffer.CopyArea (buffer, + tile, + buffer.fPlane, + buffer.fPlanes); + + } + + } + +/*****************************************************************************/ + +void dng_image::GetRepeat (dng_pixel_buffer &buffer, + const dng_rect &srcArea, + const dng_rect &dstArea) const + { + + // If we already have the entire srcArea in the + // buffer, we can just repeat that. + + if ((srcArea & buffer.fArea) == srcArea) + { + + buffer.RepeatArea (srcArea, + dstArea); + + } + + // Else we first need to get the srcArea into the buffer area. + + else + { + + // Find repeating pattern size. + + dng_point repeat = srcArea.Size (); + + // Find pattern phase at top-left corner of destination area. + + dng_point phase = dng_pixel_buffer::RepeatPhase (srcArea, + dstArea); + + // Find new source area at top-left of dstArea. + + dng_rect newArea = srcArea + (dstArea.TL () - + srcArea.TL ()); + + // Find quadrant split coordinates. + + int32 splitV = newArea.t + repeat.v - phase.v; + int32 splitH = newArea.l + repeat.h - phase.h; + + // Top-left quadrant. + + dng_rect dst1 (dng_rect (newArea.t, + newArea.l, + splitV, + splitH) & dstArea); + + if (dst1.NotEmpty ()) + { + + dng_pixel_buffer temp (buffer); + + temp.fArea = dst1 + (srcArea.TL () - + dstArea.TL () + + dng_point (phase.v, phase.h)); + + temp.fData = buffer.DirtyPixel (dst1.t, + dst1.l, + buffer.fPlane); + + DoGet (temp); + + } + + // Top-right quadrant. + + dng_rect dst2 (dng_rect (newArea.t, + splitH, + splitV, + newArea.r) & dstArea); + + if (dst2.NotEmpty ()) + { + + dng_pixel_buffer temp (buffer); + + temp.fArea = dst2 + (srcArea.TL () - + dstArea.TL () + + dng_point (phase.v, -phase.h)); + + temp.fData = buffer.DirtyPixel (dst2.t, + dst2.l, + buffer.fPlane); + + DoGet (temp); + + } + + // Bottom-left quadrant. + + dng_rect dst3 (dng_rect (splitV, + newArea.l, + newArea.b, + splitH) & dstArea); + + if (dst3.NotEmpty ()) + { + + dng_pixel_buffer temp (buffer); + + temp.fArea = dst3 + (srcArea.TL () - + dstArea.TL () + + dng_point (-phase.v, phase.h)); + + temp.fData = buffer.DirtyPixel (dst3.t, + dst3.l, + buffer.fPlane); + + DoGet (temp); + + } + + // Bottom-right quadrant. + + dng_rect dst4 (dng_rect (splitV, + splitH, + newArea.b, + newArea.r) & dstArea); + + if (dst4.NotEmpty ()) + { + + dng_pixel_buffer temp (buffer); + + temp.fArea = dst4 + (srcArea.TL () - + dstArea.TL () + + dng_point (-phase.v, -phase.h)); + + temp.fData = buffer.DirtyPixel (dst4.t, + dst4.l, + buffer.fPlane); + + DoGet (temp); + + } + + // Replicate this new source area. + + buffer.RepeatArea (newArea, + dstArea); + + } + + } + +/*****************************************************************************/ + +void dng_image::GetEdge (dng_pixel_buffer &buffer, + edge_option edgeOption, + const dng_rect &srcArea, + const dng_rect &dstArea) const + { + + switch (edgeOption) + { + + case edge_zero: + { + + buffer.SetZero (dstArea, + buffer.fPlane, + buffer.fPlanes); + + break; + + } + + case edge_repeat: + { + + GetRepeat (buffer, + srcArea, + dstArea); + + break; + + } + + case edge_repeat_zero_last: + { + + if (buffer.fPlanes > 1) + { + + dng_pixel_buffer buffer1 (buffer); + + buffer1.fPlanes--; + + GetEdge (buffer1, + edge_repeat, + srcArea, + dstArea); + + } + + dng_pixel_buffer buffer2 (buffer); + + buffer2.fPlane = buffer.fPlanes - 1; + buffer2.fPlanes = 1; + + buffer2.fData = buffer.DirtyPixel (buffer2.fArea.t, + buffer2.fArea.l, + buffer2.fPlane); + + GetEdge (buffer2, + edge_zero, + srcArea, + dstArea); + + break; + + } + + default: + { + + ThrowProgramError (); + + } + + } + + } + +/*****************************************************************************/ + +void dng_image::Get (dng_pixel_buffer &buffer, + edge_option edgeOption, + uint32 repeatV, + uint32 repeatH) const + { + + // Find the overlap with the image bounds. + + dng_rect overlap = buffer.fArea & fBounds; + + // Move the overlapping pixels. + + if (overlap.NotEmpty ()) + { + + dng_pixel_buffer temp (buffer); + + temp.fArea = overlap; + + temp.fData = buffer.DirtyPixel (overlap.t, + overlap.l, + buffer.fPlane); + + DoGet (temp); + + } + + // See if we need to pad the edge values. + + if ((edgeOption != edge_none) && (overlap != buffer.fArea)) + { + + dng_rect areaT (buffer.fArea); + dng_rect areaL (buffer.fArea); + dng_rect areaB (buffer.fArea); + dng_rect areaR (buffer.fArea); + + areaT.b = Min_int32 (areaT.b, fBounds.t); + areaL.r = Min_int32 (areaL.r, fBounds.l); + areaB.t = Max_int32 (areaB.t, fBounds.b); + areaR.l = Max_int32 (areaR.l, fBounds.r); + + dng_rect areaH (buffer.fArea); + dng_rect areaV (buffer.fArea); + + areaH.l = Max_int32 (areaH.l, fBounds.l); + areaH.r = Min_int32 (areaH.r, fBounds.r); + + areaV.t = Max_int32 (areaV.t, fBounds.t); + areaV.b = Min_int32 (areaV.b, fBounds.b); + + // Top left. + + dng_rect areaTL = areaT & areaL; + + if (areaTL.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (fBounds.t, + fBounds.l, + fBounds.t + repeatV, + fBounds.l + repeatH), + areaTL); + + } + + // Top middle. + + dng_rect areaTM = areaT & areaH; + + if (areaTM.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (fBounds.t, + areaTM.l, + fBounds.t + repeatV, + areaTM.r), + areaTM); + + } + + // Top right. + + dng_rect areaTR = areaT & areaR; + + if (areaTR.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (fBounds.t, + fBounds.r - repeatH, + fBounds.t + repeatV, + fBounds.r), + areaTR); + + } + + // Left middle. + + dng_rect areaLM = areaL & areaV; + + if (areaLM.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (areaLM.t, + fBounds.l, + areaLM.b, + fBounds.l + repeatH), + areaLM); + + } + + // Right middle. + + dng_rect areaRM = areaR & areaV; + + if (areaRM.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (areaRM.t, + fBounds.r - repeatH, + areaRM.b, + fBounds.r), + areaRM); + + } + + // Bottom left. + + dng_rect areaBL = areaB & areaL; + + if (areaBL.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (fBounds.b - repeatV, + fBounds.l, + fBounds.b, + fBounds.l + repeatH), + areaBL); + + } + + // Bottom middle. + + dng_rect areaBM = areaB & areaH; + + if (areaBM.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (fBounds.b - repeatV, + areaBM.l, + fBounds.b, + areaBM.r), + areaBM); + + } + + // Bottom right. + + dng_rect areaBR = areaB & areaR; + + if (areaBR.NotEmpty ()) + { + + GetEdge (buffer, + edgeOption, + dng_rect (fBounds.b - repeatV, + fBounds.r - repeatH, + fBounds.b, + fBounds.r), + areaBR); + + } + + } + + } + +/*****************************************************************************/ + +void dng_image::Put (const dng_pixel_buffer &buffer) + { + + // Move the overlapping pixels. + + dng_rect overlap = buffer.fArea & fBounds; + + if (overlap.NotEmpty ()) + { + + dng_pixel_buffer temp (buffer); + + temp.fArea = overlap; + + temp.fData = (void *) buffer.ConstPixel (overlap.t, + overlap.l, + buffer.fPlane); + + // Move the overlapping planes. + + if (temp.fPlane < Planes ()) + { + + temp.fPlanes = Min_uint32 (temp.fPlanes, + Planes () - temp.fPlane); + + DoPut (temp); + + } + + } + + } + +/*****************************************************************************/ + +void dng_image::Trim (const dng_rect &r) + { + + if (r != Bounds ()) + { + + ThrowProgramError ("Trim is not support by this dng_image subclass"); + + } + + } + +/*****************************************************************************/ + +void dng_image::Rotate (const dng_orientation &orientation) + { + + if (orientation != dng_orientation::Normal ()) + { + + ThrowProgramError ("Rotate is not support by this dng_image subclass"); + + } + + } + +/*****************************************************************************/ + +void dng_image::CopyArea (const dng_image &src, + const dng_rect &area, + uint32 srcPlane, + uint32 dstPlane, + uint32 planes) + { + + if (&src == this) + return; + + dng_tile_iterator destIter(*this, area); + dng_rect destTileArea; + + while (destIter.GetOneTile(destTileArea)) + { + dng_tile_iterator srcIter(src, destTileArea); + dng_rect srcTileArea; + + while (srcIter.GetOneTile(srcTileArea)) + { + + dng_dirty_tile_buffer destTile(*this, srcTileArea); + dng_const_tile_buffer srcTile(src, srcTileArea); + + destTile.CopyArea (srcTile, srcTileArea, srcPlane, dstPlane, planes); + + } + + } + + } + +/*****************************************************************************/ + +bool dng_image::EqualArea (const dng_image &src, + const dng_rect &area, + uint32 plane, + uint32 planes) const + { + + if (&src == this) + return true; + + dng_tile_iterator destIter (*this, area); + + dng_rect destTileArea; + + while (destIter.GetOneTile (destTileArea)) + { + + dng_tile_iterator srcIter (src, destTileArea); + + dng_rect srcTileArea; + + while (srcIter.GetOneTile (srcTileArea)) + { + + dng_const_tile_buffer destTile (*this, srcTileArea); + dng_const_tile_buffer srcTile (src , srcTileArea); + + if (!destTile.EqualArea (srcTile, srcTileArea, plane, planes)) + { + return false; + } + + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_image::SetConstant (uint32 value, + const dng_rect &area) + { + + dng_tile_iterator iter (*this, area); + + dng_rect tileArea; + + while (iter.GetOneTile (tileArea)) + { + + dng_dirty_tile_buffer buffer (*this, tileArea); + + buffer.SetConstant (tileArea, + 0, + fPlanes, + value); + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_image.h b/source/lib/dng_sdk/dng_image.h new file mode 100644 index 0000000..013182d --- /dev/null +++ b/source/lib/dng_sdk/dng_image.h @@ -0,0 +1,433 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_image.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Support for working with image data in DNG SDK. + */ + +/*****************************************************************************/ + +#ifndef __dng_image__ +#define __dng_image__ + +/*****************************************************************************/ + +#include "dng_assertions.h" +#include "dng_classes.h" +#include "dng_pixel_buffer.h" +#include "dng_point.h" +#include "dng_rect.h" +#include "dng_tag_types.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief Class to get resource acquisition is instantiation behavior for tile +/// buffers. Can be dirty or constant tile access. + +class dng_tile_buffer: public dng_pixel_buffer + { + + protected: + + const dng_image &fImage; + + void *fRefData; + + protected: + + /// Obtain a tile from an image. + /// \param image Image tile will come from. + /// \param tile Rectangle denoting extent of tile. + /// \param dirty Flag indicating whether this is read-only or read-write acesss. + + dng_tile_buffer (const dng_image &image, + const dng_rect &tile, + bool dirty); + + virtual ~dng_tile_buffer (); + + public: + + void SetRefData (void *refData) + { + fRefData = refData; + } + + void * GetRefData () const + { + return fRefData; + } + + private: + + // Hidden copy constructor and assignment operator. + + dng_tile_buffer (const dng_tile_buffer &buffer); + + dng_tile_buffer & operator= (const dng_tile_buffer &buffer); + + }; + +/*****************************************************************************/ + +/// \brief Class to get resource acquisition is instantiation behavior for +/// constant (read-only) tile buffers. + +class dng_const_tile_buffer: public dng_tile_buffer + { + + public: + + /// Obtain a read-only tile from an image. + /// \param image Image tile will come from. + /// \param tile Rectangle denoting extent of tile. + + dng_const_tile_buffer (const dng_image &image, + const dng_rect &tile); + + virtual ~dng_const_tile_buffer (); + + }; + +/*****************************************************************************/ + +/// \brief Class to get resource acquisition is instantiation behavior for +/// dirty (writable) tile buffers. + +class dng_dirty_tile_buffer: public dng_tile_buffer + { + + public: + + /// Obtain a writable tile from an image. + /// \param image Image tile will come from. + /// \param tile Rectangle denoting extent of tile. + + dng_dirty_tile_buffer (dng_image &image, + const dng_rect &tile); + + virtual ~dng_dirty_tile_buffer (); + + }; + +/*****************************************************************************/ + +/// \brief Base class for holding image data in DNG SDK. See dng_simple_image +/// for derived class most often used in DNG SDK. + +class dng_image + { + + friend class dng_tile_buffer; + + protected: + + // Bounds for this image. + + dng_rect fBounds; + + // Number of image planes. + + uint32 fPlanes; + + // Basic pixel type (TIFF tag type code). + + uint32 fPixelType; + + public: + + /// How to handle requests to get image areas outside the image bounds. + + enum edge_option + { + + /// Leave edge pixels unchanged. + + edge_none, + + /// Pad with zeros. + + edge_zero, + + /// Repeat edge pixels. + + edge_repeat, + + /// Repeat edge pixels, except for last plane which is zero padded. + + edge_repeat_zero_last + + }; + + protected: + + dng_image (const dng_rect &bounds, + uint32 planes, + uint32 pixelType); + + public: + + virtual ~dng_image (); + + virtual dng_image * Clone () const; + + /// Getter method for bounds of an image. + + const dng_rect & Bounds () const + { + return fBounds; + } + + /// Getter method for size of an image. + + dng_point Size () const + { + return Bounds ().Size (); + } + + /// Getter method for width of an image. + + uint32 Width () const + { + return Bounds ().W (); + } + + /// Getter method for height of an image. + + uint32 Height () const + { + return Bounds ().H (); + } + + /// Getter method for number of planes in an image. + + uint32 Planes () const + { + return fPlanes; + } + + /// Getter for pixel type. + /// \retval See dng_tagtypes.h . Valid values are ttByte, ttShort, ttSShort, + /// ttLong, ttFloat . + + uint32 PixelType () const + { + return fPixelType; + } + + /// Setter for pixel type. + /// \param pixelType The new pixel type . + + virtual void SetPixelType (uint32 pixelType); + + /// Getter for pixel size. + /// \retval Size, in bytes, of pixel type for this image . + + uint32 PixelSize () const; + + /// Getter for pixel range. + /// For unsigned types, range is 0 to return value. + /// For signed types, range is return value - 0x8000U. + /// For ttFloat type, pixel range is 0.0 to 1.0 and this routine returns 1. + + uint32 PixelRange () const; + + /// Getter for best "tile stride" for accessing image. + + virtual dng_rect RepeatingTile () const; + + /// Get a pixel buffer of data on image with proper edge padding. + /// \param buffer Receives resulting pixel buffer. + /// \param edgeOption edge_option describing how to pad edges. + /// \param repeatV Amount of repeated padding needed in vertical for + /// edge_repeat and edge_repeat_zero_last edgeOption cases. + /// \param repeatH Amount of repeated padding needed in horizontal for + /// edge_repeat and edge_repeat_zero_last edgeOption cases. + + void Get (dng_pixel_buffer &buffer, + edge_option edgeOption = edge_none, + uint32 repeatV = 1, + uint32 repeatH = 1) const; + + /// Put a pixel buffer into image. + /// \param buffer Pixel buffer to copy from. + + void Put (const dng_pixel_buffer &buffer); + + /// Shrink bounds of image to given rectangle. + /// \param r Rectangle to crop to. + + virtual void Trim (const dng_rect &r); + + /// Rotate image to reflect given orientation change. + /// \param orientation Directive to rotate image in a certain way. + + virtual void Rotate (const dng_orientation &orientation); + + /// Copy image data from an area of one image to same area of another. + /// \param src Image to copy from. + /// \param area Rectangle of images to copy. + /// \param srcPlane Plane to start copying in src. + /// \param dstPlane Plane to start copying in this. + /// \param planes Number of planes to copy. + + void CopyArea (const dng_image &src, + const dng_rect &area, + uint32 srcPlane, + uint32 dstPlane, + uint32 planes); + + /// Copy image data from an area of one image to same area of another. + /// \param src Image to copy from. + /// \param area Rectangle of images to copy. + /// \param plane Plane to start copying in src and this. + /// \param planes Number of planes to copy. + + void CopyArea (const dng_image &src, + const dng_rect &area, + uint32 plane, + uint32 planes) + { + + CopyArea (src, area, plane, plane, planes); + + } + + /// Return true if the contents of an area of the image are the same as those of another. + /// \param rhs Image to compare against. + /// \param area Rectangle of image to test. + /// \param plane Plane to start comparing. + /// \param planes Number of planes to compare. + + bool EqualArea (const dng_image &rhs, + const dng_rect &area, + uint32 plane, + uint32 planes) const; + + // Routines to set the entire image to a constant value. + + void SetConstant_uint8 (uint8 value, + const dng_rect &area) + { + + DNG_ASSERT (fPixelType == ttByte, "Mismatched pixel type"); + + SetConstant ((uint32) value, area); + + } + + void SetConstant_uint8 (uint8 value) + { + SetConstant (value, Bounds ()); + } + + void SetConstant_uint16 (uint16 value, + const dng_rect &area) + { + + DNG_ASSERT (fPixelType == ttShort, "Mismatched pixel type"); + + SetConstant ((uint32) value, area); + + } + + void SetConstant_uint16 (uint16 value) + { + SetConstant_uint16 (value, Bounds ()); + } + + void SetConstant_int16 (int16 value, + const dng_rect &area) + { + + DNG_ASSERT (fPixelType == ttSShort, "Mismatched pixel type"); + + SetConstant ((uint32) (uint16) value, area); + + } + + void SetConstant_int16 (int16 value) + { + SetConstant_int16 (value, Bounds ()); + } + + void SetConstant_uint32 (uint32 value, + const dng_rect &area) + { + + DNG_ASSERT (fPixelType == ttLong, "Mismatched pixel type"); + + SetConstant (value, area); + + } + + void SetConstant_uint32 (uint32 value) + { + SetConstant_uint32 (value, Bounds ()); + } + + void SetConstant_real32 (real32 value, + const dng_rect &area) + { + + DNG_ASSERT (fPixelType == ttFloat, "Mismatched pixel type"); + + union + { + uint32 i; + real32 f; + } x; + + x.f = value; + + SetConstant (x.i, area); + + } + + void SetConstant_real32 (real32 value) + { + SetConstant_real32 (value, Bounds ()); + } + + virtual void GetRepeat (dng_pixel_buffer &buffer, + const dng_rect &srcArea, + const dng_rect &dstArea) const; + + protected: + + virtual void AcquireTileBuffer (dng_tile_buffer &buffer, + const dng_rect &area, + bool dirty) const; + + virtual void ReleaseTileBuffer (dng_tile_buffer &buffer) const; + + virtual void DoGet (dng_pixel_buffer &buffer) const; + + virtual void DoPut (const dng_pixel_buffer &buffer); + + void GetEdge (dng_pixel_buffer &buffer, + edge_option edgeOption, + const dng_rect &srcArea, + const dng_rect &dstArea) const; + + virtual void SetConstant (uint32 value, + const dng_rect &area); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_image_writer.cpp b/source/lib/dng_sdk/dng_image_writer.cpp new file mode 100644 index 0000000..22d65af --- /dev/null +++ b/source/lib/dng_sdk/dng_image_writer.cpp @@ -0,0 +1,6467 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_image_writer.cpp#4 $ */ +/* $DateTime: 2012/06/14 20:24:41 $ */ +/* $Change: 835078 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_image_writer.h" + +#include "dng_abort_sniffer.h" +#include "dng_area_task.h" +#include "dng_bottlenecks.h" +#include "dng_camera_profile.h" +#include "dng_color_space.h" +#include "dng_exif.h" +#include "dng_flags.h" +#include "dng_exceptions.h" +#include "dng_host.h" +#include "dng_ifd.h" +#include "dng_image.h" +#include "dng_jpeg_image.h" +#include "dng_lossless_jpeg.h" +#include "dng_memory_stream.h" +#include "dng_negative.h" +#include "dng_pixel_buffer.h" +#include "dng_preview.h" +#include "dng_read_image.h" +#include "dng_stream.h" +#include "dng_string_list.h" +#include "dng_tag_codes.h" +#include "dng_tag_values.h" +#include "dng_utils.h" +#include "dng_xmp.h" + +#include "stdc_includes.h" + +#include "log.h" + +/*****************************************************************************/ + +// Defines for testing DNG 1.2 features. + +//#define qTestRowInterleave 2 + +//#define qTestSubTileBlockRows 2 +//#define qTestSubTileBlockCols 2 + +/*****************************************************************************/ + +dng_resolution::dng_resolution () + + : fXResolution () + , fYResolution () + + , fResolutionUnit (0) + + { + + } + +/******************************************************************************/ + +static void SpoolAdobeData (dng_stream &stream, + const dng_metadata *metadata, + const dng_jpeg_preview *preview, + const dng_memory_block *imageResources) + { + + TempBigEndian tempEndian (stream); + + if (metadata && metadata->GetXMP ()) + { + + bool marked = false; + + if (metadata->GetXMP ()->GetBoolean (XMP_NS_XAP_RIGHTS, + "Marked", + marked)) + { + + stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M')); + stream.Put_uint16 (1034); + stream.Put_uint16 (0); + + stream.Put_uint32 (1); + + stream.Put_uint8 (marked ? 1 : 0); + + stream.Put_uint8 (0); + + } + + dng_string webStatement; + + if (metadata->GetXMP ()->GetString (XMP_NS_XAP_RIGHTS, + "WebStatement", + webStatement)) + { + + dng_memory_data buffer; + + uint32 size = webStatement.Get_SystemEncoding (buffer); + + if (size > 0) + { + + stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M')); + stream.Put_uint16 (1035); + stream.Put_uint16 (0); + + stream.Put_uint32 (size); + + stream.Put (buffer.Buffer (), size); + + if (size & 1) + stream.Put_uint8 (0); + + } + + } + + } + + if (preview) + { + + preview->SpoolAdobeThumbnail (stream); + + } + + if (metadata && metadata->IPTCLength ()) + { + + dng_fingerprint iptcDigest = metadata->IPTCDigest (); + + if (iptcDigest.IsValid ()) + { + + stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M')); + stream.Put_uint16 (1061); + stream.Put_uint16 (0); + + stream.Put_uint32 (16); + + stream.Put (iptcDigest.data, 16); + + } + + } + + if (imageResources) + { + + uint32 size = imageResources->LogicalSize (); + + stream.Put (imageResources->Buffer (), size); + + if (size & 1) + stream.Put_uint8 (0); + + } + + } + +/******************************************************************************/ + +static dng_memory_block * BuildAdobeData (dng_host &host, + const dng_metadata *metadata, + const dng_jpeg_preview *preview, + const dng_memory_block *imageResources) + { + + dng_memory_stream stream (host.Allocator ()); + + SpoolAdobeData (stream, + metadata, + preview, + imageResources); + + return stream.AsMemoryBlock (host.Allocator ()); + + } + +/*****************************************************************************/ + +tag_string::tag_string (uint16 code, + const dng_string &s, + bool forceASCII) + + : tiff_tag (code, ttAscii, 0) + + , fString (s) + + { + + if (forceASCII) + { + + // Metadata working group recommendation - go ahead + // write UTF-8 into ASCII tag strings, rather than + // actually force the strings to ASCII. There is a matching + // change on the reading side to assume UTF-8 if the string + // contains a valid UTF-8 string. + // + // fString.ForceASCII (); + + } + + else if (!fString.IsASCII ()) + { + + fType = ttByte; + + } + + fCount = fString.Length () + 1; + + } + +/*****************************************************************************/ + +void tag_string::Put (dng_stream &stream) const + { + + stream.Put (fString.Get (), Size ()); + + } + +/*****************************************************************************/ + +tag_encoded_text::tag_encoded_text (uint16 code, + const dng_string &text) + + : tiff_tag (code, ttUndefined, 0) + + , fText (text) + + , fUTF16 () + + { + + if (fText.IsASCII ()) + { + + fCount = 8 + fText.Length (); + + } + + else + { + + fCount = 8 + fText.Get_UTF16 (fUTF16) * 2; + + } + + } + +/*****************************************************************************/ + +void tag_encoded_text::Put (dng_stream &stream) const + { + + if (fUTF16.Buffer ()) + { + + stream.Put ("UNICODE\000", 8); + + uint32 chars = (fCount - 8) >> 1; + + const uint16 *buf = fUTF16.Buffer_uint16 (); + + for (uint32 j = 0; j < chars; j++) + { + + stream.Put_uint16 (buf [j]); + + } + + } + + else + { + + stream.Put ("ASCII\000\000\000", 8); + + stream.Put (fText.Get (), fCount - 8); + + } + + } + +/*****************************************************************************/ + +void tag_data_ptr::Put (dng_stream &stream) const + { + + // If we are swapping bytes, we need to swap with the right size + // entries. + + if (stream.SwapBytes ()) + { + + switch (Type ()) + { + + // Two byte entries. + + case ttShort: + case ttSShort: + case ttUnicode: + { + + const uint16 *p = (const uint16 *) fData; + + uint32 entries = (Size () >> 1); + + for (uint32 j = 0; j < entries; j++) + { + + stream.Put_uint16 (p [j]); + + } + + return; + + } + + // Four byte entries. + + case ttLong: + case ttSLong: + case ttRational: + case ttSRational: + case ttIFD: + case ttFloat: + case ttComplex: + { + + const uint32 *p = (const uint32 *) fData; + + uint32 entries = (Size () >> 2); + + for (uint32 j = 0; j < entries; j++) + { + + stream.Put_uint32 (p [j]); + + } + + return; + + } + + // Eight byte entries. + + case ttDouble: + { + + const real64 *p = (const real64 *) fData; + + uint32 entries = (Size () >> 3); + + for (uint32 j = 0; j < entries; j++) + { + + stream.Put_real64 (p [j]); + + } + + return; + + } + + // Entries don't need to be byte swapped. Fall through + // to non-byte swapped case. + + default: + { + + break; + + } + + } + + } + + // Non-byte swapped case. + + stream.Put (fData, Size ()); + + } + +/******************************************************************************/ + +tag_matrix::tag_matrix (uint16 code, + const dng_matrix &m) + + : tag_srational_ptr (code, fEntry, m.Rows () * m.Cols ()) + + { + + uint32 index = 0; + + for (uint32 r = 0; r < m.Rows (); r++) + for (uint32 c = 0; c < m.Cols (); c++) + { + + fEntry [index].Set_real64 (m [r] [c], 10000); + + index++; + + } + + } + +/******************************************************************************/ + +tag_icc_profile::tag_icc_profile (const void *profileData, + uint32 profileSize) + + : tag_data_ptr (tcICCProfile, + ttUndefined, + 0, + NULL) + + { + + if (profileData && profileSize) + { + + SetCount (profileSize); + SetData (profileData); + + } + + } + +/******************************************************************************/ + +void tag_cfa_pattern::Put (dng_stream &stream) const + { + + stream.Put_uint16 ((uint16) fCols); + stream.Put_uint16 ((uint16) fRows); + + for (uint32 col = 0; col < fCols; col++) + for (uint32 row = 0; row < fRows; row++) + { + + stream.Put_uint8 (fPattern [row * kMaxCFAPattern + col]); + + } + + } + +/******************************************************************************/ + +tag_exif_date_time::tag_exif_date_time (uint16 code, + const dng_date_time &dt) + + : tag_data_ptr (code, ttAscii, 20, fData) + + { + + if (dt.IsValid ()) + { + + sprintf (fData, + "%04d:%02d:%02d %02d:%02d:%02d", + (int) dt.fYear, + (int) dt.fMonth, + (int) dt.fDay, + (int) dt.fHour, + (int) dt.fMinute, + (int) dt.fSecond); + + } + + } + +/******************************************************************************/ + +tag_iptc::tag_iptc (const void *data, + uint32 length) + + : tiff_tag (tcIPTC_NAA, ttLong, (length + 3) >> 2) + + , fData (data ) + , fLength (length) + + { + + } + +/******************************************************************************/ + +void tag_iptc::Put (dng_stream &stream) const + { + + // Note: For historical compatiblity reasons, the standard TIFF data + // type for IPTC data is ttLong, but without byte swapping. This really + // should be ttUndefined, but doing the right thing would break some + // existing readers. + + stream.Put (fData, fLength); + + // Pad with zeros to get to long word boundary. + + uint32 extra = fCount * 4 - fLength; + + while (extra--) + { + stream.Put_uint8 (0); + } + + } + +/******************************************************************************/ + +tag_xmp::tag_xmp (const dng_xmp *xmp) + + : tag_uint8_ptr (tcXMP, NULL, 0) + + , fBuffer () + + { + + if (xmp) + { + + fBuffer.Reset (xmp->Serialize (true)); + + if (fBuffer.Get ()) + { + + SetData (fBuffer->Buffer_uint8 ()); + + SetCount (fBuffer->LogicalSize ()); + + } + + } + + } + +/******************************************************************************/ + +void dng_tiff_directory::Add (const tiff_tag *tag) + { + + if (fEntries >= kMaxEntries) + { + ThrowProgramError (); + } + + // Tags must be sorted in increasing order of tag code. + + uint32 index = fEntries; + + for (uint32 j = 0; j < fEntries; j++) + { + + if (tag->Code () < fTag [j]->Code ()) + { + index = j; + break; + } + + } + + for (uint32 k = fEntries; k > index; k--) + { + + fTag [k] = fTag [k - 1]; + + } + + fTag [index] = tag; + + fEntries++; + + } + +/******************************************************************************/ + +uint32 dng_tiff_directory::Size () const + { + + if (!fEntries) return 0; + + uint32 size = fEntries * 12 + 6; + + for (uint32 index = 0; index < fEntries; index++) + { + + uint32 tagSize = fTag [index]->Size (); + + if (tagSize > 4) + { + + size += (tagSize + 1) & ~1; + + } + + } + + return size; + + } + +/******************************************************************************/ + +void dng_tiff_directory::Put (dng_stream &stream, + OffsetsBase offsetsBase, + uint32 explicitBase) const + { + + if (!fEntries) return; + + uint32 index; + + uint32 bigData = fEntries * 12 + 6; + + if (offsetsBase == offsetsRelativeToStream) + bigData += (uint32) stream.Position (); + + else if (offsetsBase == offsetsRelativeToExplicitBase) + bigData += explicitBase; + + stream.Put_uint16 ((uint16) fEntries); + + for (index = 0; index < fEntries; index++) + { + + const tiff_tag &tag = *fTag [index]; + + stream.Put_uint16 (tag.Code ()); + stream.Put_uint16 (tag.Type ()); + stream.Put_uint32 (tag.Count ()); + + uint32 size = tag.Size (); + + if (size <= 4) + { + + tag.Put (stream); + + while (size < 4) + { + stream.Put_uint8 (0); + size++; + } + + } + + else + { + + stream.Put_uint32 (bigData); + + bigData += (size + 1) & ~1; + + } + + } + + stream.Put_uint32 (fChained); // Next IFD offset + + for (index = 0; index < fEntries; index++) + { + + const tiff_tag &tag = *fTag [index]; + + uint32 size = tag.Size (); + + if (size > 4) + { + + tag.Put (stream); + + if (size & 1) + stream.Put_uint8 (0); + + } + + } + + } + +/******************************************************************************/ + +dng_basic_tag_set::dng_basic_tag_set (dng_tiff_directory &directory, + const dng_ifd &info) + + : fNewSubFileType (tcNewSubFileType, info.fNewSubFileType) + + , fImageWidth (tcImageWidth , info.fImageWidth ) + , fImageLength (tcImageLength, info.fImageLength) + + , fPhotoInterpretation (tcPhotometricInterpretation, + (uint16) info.fPhotometricInterpretation) + + , fFillOrder (tcFillOrder, 1) + + , fSamplesPerPixel (tcSamplesPerPixel, (uint16) info.fSamplesPerPixel) + + , fBitsPerSample (tcBitsPerSample, + fBitsPerSampleData, + info.fSamplesPerPixel) + + , fStrips (info.fUsesStrips) + + , fTileWidth (tcTileWidth, info.fTileWidth) + + , fTileLength (fStrips ? tcRowsPerStrip : tcTileLength, + info.fTileLength) + + , fTileInfoBuffer (info.TilesPerImage () * 8) + + , fTileOffsetData (fTileInfoBuffer.Buffer_uint32 ()) + + , fTileOffsets (fStrips ? tcStripOffsets : tcTileOffsets, + fTileOffsetData, + info.TilesPerImage ()) + + , fTileByteCountData (fTileOffsetData + info.TilesPerImage ()) + + , fTileByteCounts (fStrips ? tcStripByteCounts : tcTileByteCounts, + fTileByteCountData, + info.TilesPerImage ()) + + , fPlanarConfiguration (tcPlanarConfiguration, pcInterleaved) + + , fCompression (tcCompression, (uint16) info.fCompression) + , fPredictor (tcPredictor , (uint16) info.fPredictor ) + + , fExtraSamples (tcExtraSamples, + fExtraSamplesData, + info.fExtraSamplesCount) + + , fSampleFormat (tcSampleFormat, + fSampleFormatData, + info.fSamplesPerPixel) + + , fRowInterleaveFactor (tcRowInterleaveFactor, + (uint16) info.fRowInterleaveFactor) + + , fSubTileBlockSize (tcSubTileBlockSize, + fSubTileBlockSizeData, + 2) + + { + + uint32 j; + + for (j = 0; j < info.fSamplesPerPixel; j++) + { + + fBitsPerSampleData [j] = (uint16) info.fBitsPerSample [0]; + + } + + directory.Add (&fNewSubFileType); + + directory.Add (&fImageWidth); + directory.Add (&fImageLength); + + directory.Add (&fPhotoInterpretation); + + directory.Add (&fSamplesPerPixel); + + directory.Add (&fBitsPerSample); + + if (info.fBitsPerSample [0] != 8 && + info.fBitsPerSample [0] != 16 && + info.fBitsPerSample [0] != 32) + { + + directory.Add (&fFillOrder); + + } + + if (!fStrips) + { + + directory.Add (&fTileWidth); + + } + + directory.Add (&fTileLength); + + directory.Add (&fTileOffsets); + directory.Add (&fTileByteCounts); + + directory.Add (&fPlanarConfiguration); + + directory.Add (&fCompression); + + if (info.fPredictor != cpNullPredictor) + { + + directory.Add (&fPredictor); + + } + + if (info.fExtraSamplesCount != 0) + { + + for (j = 0; j < info.fExtraSamplesCount; j++) + { + fExtraSamplesData [j] = (uint16) info.fExtraSamples [j]; + } + + directory.Add (&fExtraSamples); + + } + + if (info.fSampleFormat [0] != sfUnsignedInteger) + { + + for (j = 0; j < info.fSamplesPerPixel; j++) + { + fSampleFormatData [j] = (uint16) info.fSampleFormat [j]; + } + + directory.Add (&fSampleFormat); + + } + + if (info.fRowInterleaveFactor != 1) + { + + directory.Add (&fRowInterleaveFactor); + + } + + if (info.fSubTileBlockRows != 1 || + info.fSubTileBlockCols != 1) + { + + fSubTileBlockSizeData [0] = (uint16) info.fSubTileBlockRows; + fSubTileBlockSizeData [1] = (uint16) info.fSubTileBlockCols; + + directory.Add (&fSubTileBlockSize); + + } + + } + +/******************************************************************************/ + +exif_tag_set::exif_tag_set (dng_tiff_directory &directory, + const dng_exif &exif, + bool makerNoteSafe, + const void *makerNoteData, + uint32 makerNoteLength, + bool insideDNG) + + : fExifIFD () + , fGPSIFD () + + , fExifLink (tcExifIFD, 0) + , fGPSLink (tcGPSInfo, 0) + + , fAddedExifLink (false) + , fAddedGPSLink (false) + + , fExifVersion (tcExifVersion, ttUndefined, 4, fExifVersionData) + + , fExposureTime (tcExposureTime , exif.fExposureTime ) + , fShutterSpeedValue (tcShutterSpeedValue, exif.fShutterSpeedValue) + + , fFNumber (tcFNumber , exif.fFNumber ) + , fApertureValue (tcApertureValue, exif.fApertureValue) + + , fBrightnessValue (tcBrightnessValue, exif.fBrightnessValue) + + , fExposureBiasValue (tcExposureBiasValue, exif.fExposureBiasValue) + + , fMaxApertureValue (tcMaxApertureValue , exif.fMaxApertureValue) + + , fSubjectDistance (tcSubjectDistance, exif.fSubjectDistance) + + , fFocalLength (tcFocalLength, exif.fFocalLength) + + // Special case: the EXIF 2.2 standard represents ISO speed ratings with 2 bytes, + // which cannot hold ISO speed ratings above 65535 (e.g., 102400). In these + // cases, we write the maximum representable ISO speed rating value in the EXIF + // tag, i.e., 65535. + + , fISOSpeedRatings (tcISOSpeedRatings, + (uint16) Min_uint32 (65535, + exif.fISOSpeedRatings [0])) + + , fSensitivityType (tcSensitivityType, (uint16) exif.fSensitivityType) + + , fStandardOutputSensitivity (tcStandardOutputSensitivity, exif.fStandardOutputSensitivity) + + , fRecommendedExposureIndex (tcRecommendedExposureIndex, exif.fRecommendedExposureIndex) + + , fISOSpeed (tcISOSpeed, exif.fISOSpeed) + + , fISOSpeedLatitudeyyy (tcISOSpeedLatitudeyyy, exif.fISOSpeedLatitudeyyy) + + , fISOSpeedLatitudezzz (tcISOSpeedLatitudezzz, exif.fISOSpeedLatitudezzz) + + , fFlash (tcFlash, (uint16) exif.fFlash) + + , fExposureProgram (tcExposureProgram, (uint16) exif.fExposureProgram) + + , fMeteringMode (tcMeteringMode, (uint16) exif.fMeteringMode) + + , fLightSource (tcLightSource, (uint16) exif.fLightSource) + + , fSensingMethod (tcSensingMethodExif, (uint16) exif.fSensingMethod) + + , fFocalLength35mm (tcFocalLengthIn35mmFilm, (uint16) exif.fFocalLengthIn35mmFilm) + + , fFileSourceData ((uint8) exif.fFileSource) + , fFileSource (tcFileSource, ttUndefined, 1, &fFileSourceData) + + , fSceneTypeData ((uint8) exif.fSceneType) + , fSceneType (tcSceneType, ttUndefined, 1, &fSceneTypeData) + + , fCFAPattern (tcCFAPatternExif, + exif.fCFARepeatPatternRows, + exif.fCFARepeatPatternCols, + &exif.fCFAPattern [0] [0]) + + , fCustomRendered (tcCustomRendered , (uint16) exif.fCustomRendered ) + , fExposureMode (tcExposureMode , (uint16) exif.fExposureMode ) + , fWhiteBalance (tcWhiteBalance , (uint16) exif.fWhiteBalance ) + , fSceneCaptureType (tcSceneCaptureType , (uint16) exif.fSceneCaptureType ) + , fGainControl (tcGainControl , (uint16) exif.fGainControl ) + , fContrast (tcContrast , (uint16) exif.fContrast ) + , fSaturation (tcSaturation , (uint16) exif.fSaturation ) + , fSharpness (tcSharpness , (uint16) exif.fSharpness ) + , fSubjectDistanceRange (tcSubjectDistanceRange, (uint16) exif.fSubjectDistanceRange) + + , fDigitalZoomRatio (tcDigitalZoomRatio, exif.fDigitalZoomRatio) + + , fExposureIndex (tcExposureIndexExif, exif.fExposureIndex) + + , fImageNumber (tcImageNumber, exif.fImageNumber) + + , fSelfTimerMode (tcSelfTimerMode, (uint16) exif.fSelfTimerMode) + + , fBatteryLevelA (tcBatteryLevel, exif.fBatteryLevelA) + , fBatteryLevelR (tcBatteryLevel, exif.fBatteryLevelR) + + , fFocalPlaneXResolution (tcFocalPlaneXResolutionExif, exif.fFocalPlaneXResolution) + , fFocalPlaneYResolution (tcFocalPlaneYResolutionExif, exif.fFocalPlaneYResolution) + + , fFocalPlaneResolutionUnit (tcFocalPlaneResolutionUnitExif, (uint16) exif.fFocalPlaneResolutionUnit) + + , fSubjectArea (tcSubjectArea, fSubjectAreaData, exif.fSubjectAreaCount) + + , fLensInfo (tcLensInfo, fLensInfoData, 4) + + , fDateTime (tcDateTime , exif.fDateTime .DateTime ()) + , fDateTimeOriginal (tcDateTimeOriginal , exif.fDateTimeOriginal .DateTime ()) + , fDateTimeDigitized (tcDateTimeDigitized, exif.fDateTimeDigitized.DateTime ()) + + , fSubsecTime (tcSubsecTime, exif.fDateTime .Subseconds ()) + , fSubsecTimeOriginal (tcSubsecTimeOriginal, exif.fDateTimeOriginal .Subseconds ()) + , fSubsecTimeDigitized (tcSubsecTimeDigitized, exif.fDateTimeDigitized.Subseconds ()) + + , fMake (tcMake, exif.fMake) + + , fModel (tcModel, exif.fModel) + + , fArtist (tcArtist, exif.fArtist) + + , fSoftware (tcSoftware, exif.fSoftware) + + , fCopyright (tcCopyright, exif.fCopyright) + + , fMakerNoteSafety (tcMakerNoteSafety, makerNoteSafe ? 1 : 0) + + , fMakerNote (tcMakerNote, ttUndefined, makerNoteLength, makerNoteData) + + , fImageDescription (tcImageDescription, exif.fImageDescription) + + , fSerialNumber (tcCameraSerialNumber, exif.fCameraSerialNumber) + + , fUserComment (tcUserComment, exif.fUserComment) + + , fImageUniqueID (tcImageUniqueID, ttAscii, 33, fImageUniqueIDData) + + // EXIF 2.3 tags. + + , fCameraOwnerName (tcCameraOwnerNameExif, exif.fOwnerName ) + , fBodySerialNumber (tcCameraSerialNumberExif, exif.fCameraSerialNumber) + , fLensSpecification (tcLensSpecificationExif, fLensInfoData, 4 ) + , fLensMake (tcLensMakeExif, exif.fLensMake ) + , fLensModel (tcLensModelExif, exif.fLensName ) + , fLensSerialNumber (tcLensSerialNumberExif, exif.fLensSerialNumber ) + + , fGPSVersionID (tcGPSVersionID, fGPSVersionData, 4) + + , fGPSLatitudeRef (tcGPSLatitudeRef, exif.fGPSLatitudeRef) + , fGPSLatitude (tcGPSLatitude, exif.fGPSLatitude, 3) + + , fGPSLongitudeRef (tcGPSLongitudeRef, exif.fGPSLongitudeRef) + , fGPSLongitude (tcGPSLongitude, exif.fGPSLongitude, 3) + + , fGPSAltitudeRef (tcGPSAltitudeRef, (uint8) exif.fGPSAltitudeRef) + , fGPSAltitude (tcGPSAltitude, exif.fGPSAltitude ) + + , fGPSTimeStamp (tcGPSTimeStamp, exif.fGPSTimeStamp, 3) + + , fGPSSatellites (tcGPSSatellites , exif.fGPSSatellites ) + , fGPSStatus (tcGPSStatus , exif.fGPSStatus ) + , fGPSMeasureMode (tcGPSMeasureMode, exif.fGPSMeasureMode) + + , fGPSDOP (tcGPSDOP, exif.fGPSDOP) + + , fGPSSpeedRef (tcGPSSpeedRef, exif.fGPSSpeedRef) + , fGPSSpeed (tcGPSSpeed , exif.fGPSSpeed ) + + , fGPSTrackRef (tcGPSTrackRef, exif.fGPSTrackRef) + , fGPSTrack (tcGPSTrack , exif.fGPSTrack ) + + , fGPSImgDirectionRef (tcGPSImgDirectionRef, exif.fGPSImgDirectionRef) + , fGPSImgDirection (tcGPSImgDirection , exif.fGPSImgDirection ) + + , fGPSMapDatum (tcGPSMapDatum, exif.fGPSMapDatum) + + , fGPSDestLatitudeRef (tcGPSDestLatitudeRef, exif.fGPSDestLatitudeRef) + , fGPSDestLatitude (tcGPSDestLatitude, exif.fGPSDestLatitude, 3) + + , fGPSDestLongitudeRef (tcGPSDestLongitudeRef, exif.fGPSDestLongitudeRef) + , fGPSDestLongitude (tcGPSDestLongitude, exif.fGPSDestLongitude, 3) + + , fGPSDestBearingRef (tcGPSDestBearingRef, exif.fGPSDestBearingRef) + , fGPSDestBearing (tcGPSDestBearing , exif.fGPSDestBearing ) + + , fGPSDestDistanceRef (tcGPSDestDistanceRef, exif.fGPSDestDistanceRef) + , fGPSDestDistance (tcGPSDestDistance , exif.fGPSDestDistance ) + + , fGPSProcessingMethod (tcGPSProcessingMethod, exif.fGPSProcessingMethod) + , fGPSAreaInformation (tcGPSAreaInformation , exif.fGPSAreaInformation ) + + , fGPSDateStamp (tcGPSDateStamp, exif.fGPSDateStamp) + + , fGPSDifferential (tcGPSDifferential, (uint16) exif.fGPSDifferential) + + , fGPSHPositioningError (tcGPSHPositioningError, exif.fGPSHPositioningError) + + { + + if (exif.fExifVersion) + { + + fExifVersionData [0] = (uint8) (exif.fExifVersion >> 24); + fExifVersionData [1] = (uint8) (exif.fExifVersion >> 16); + fExifVersionData [2] = (uint8) (exif.fExifVersion >> 8); + fExifVersionData [3] = (uint8) (exif.fExifVersion ); + + fExifIFD.Add (&fExifVersion); + + } + + if (exif.fExposureTime.IsValid ()) + { + fExifIFD.Add (&fExposureTime); + } + + if (exif.fShutterSpeedValue.IsValid ()) + { + fExifIFD.Add (&fShutterSpeedValue); + } + + if (exif.fFNumber.IsValid ()) + { + fExifIFD.Add (&fFNumber); + } + + if (exif.fApertureValue.IsValid ()) + { + fExifIFD.Add (&fApertureValue); + } + + if (exif.fBrightnessValue.IsValid ()) + { + fExifIFD.Add (&fBrightnessValue); + } + + if (exif.fExposureBiasValue.IsValid ()) + { + fExifIFD.Add (&fExposureBiasValue); + } + + if (exif.fMaxApertureValue.IsValid ()) + { + fExifIFD.Add (&fMaxApertureValue); + } + + if (exif.fSubjectDistance.IsValid ()) + { + fExifIFD.Add (&fSubjectDistance); + } + + if (exif.fFocalLength.IsValid ()) + { + fExifIFD.Add (&fFocalLength); + } + + if (exif.fISOSpeedRatings [0] != 0) + { + fExifIFD.Add (&fISOSpeedRatings); + } + + if (exif.fFlash <= 0x0FFFF) + { + fExifIFD.Add (&fFlash); + } + + if (exif.fExposureProgram <= 0x0FFFF) + { + fExifIFD.Add (&fExposureProgram); + } + + if (exif.fMeteringMode <= 0x0FFFF) + { + fExifIFD.Add (&fMeteringMode); + } + + if (exif.fLightSource <= 0x0FFFF) + { + fExifIFD.Add (&fLightSource); + } + + if (exif.fSensingMethod <= 0x0FFFF) + { + fExifIFD.Add (&fSensingMethod); + } + + if (exif.fFocalLengthIn35mmFilm != 0) + { + fExifIFD.Add (&fFocalLength35mm); + } + + if (exif.fFileSource <= 0x0FF) + { + fExifIFD.Add (&fFileSource); + } + + if (exif.fSceneType <= 0x0FF) + { + fExifIFD.Add (&fSceneType); + } + + if (exif.fCFARepeatPatternRows && + exif.fCFARepeatPatternCols) + { + fExifIFD.Add (&fCFAPattern); + } + + if (exif.fCustomRendered <= 0x0FFFF) + { + fExifIFD.Add (&fCustomRendered); + } + + if (exif.fExposureMode <= 0x0FFFF) + { + fExifIFD.Add (&fExposureMode); + } + + if (exif.fWhiteBalance <= 0x0FFFF) + { + fExifIFD.Add (&fWhiteBalance); + } + + if (exif.fSceneCaptureType <= 0x0FFFF) + { + fExifIFD.Add (&fSceneCaptureType); + } + + if (exif.fGainControl <= 0x0FFFF) + { + fExifIFD.Add (&fGainControl); + } + + if (exif.fContrast <= 0x0FFFF) + { + fExifIFD.Add (&fContrast); + } + + if (exif.fSaturation <= 0x0FFFF) + { + fExifIFD.Add (&fSaturation); + } + + if (exif.fSharpness <= 0x0FFFF) + { + fExifIFD.Add (&fSharpness); + } + + if (exif.fSubjectDistanceRange <= 0x0FFFF) + { + fExifIFD.Add (&fSubjectDistanceRange); + } + + if (exif.fDigitalZoomRatio.IsValid ()) + { + fExifIFD.Add (&fDigitalZoomRatio); + } + + if (exif.fExposureIndex.IsValid ()) + { + fExifIFD.Add (&fExposureIndex); + } + + if (insideDNG) // TIFF-EP only tags + { + + if (exif.fImageNumber != 0xFFFFFFFF) + { + directory.Add (&fImageNumber); + } + + if (exif.fSelfTimerMode <= 0x0FFFF) + { + directory.Add (&fSelfTimerMode); + } + + if (exif.fBatteryLevelA.NotEmpty ()) + { + directory.Add (&fBatteryLevelA); + } + + else if (exif.fBatteryLevelR.IsValid ()) + { + directory.Add (&fBatteryLevelR); + } + + } + + if (exif.fFocalPlaneXResolution.IsValid ()) + { + fExifIFD.Add (&fFocalPlaneXResolution); + } + + if (exif.fFocalPlaneYResolution.IsValid ()) + { + fExifIFD.Add (&fFocalPlaneYResolution); + } + + if (exif.fFocalPlaneResolutionUnit <= 0x0FFFF) + { + fExifIFD.Add (&fFocalPlaneResolutionUnit); + } + + if (exif.fSubjectAreaCount) + { + + fSubjectAreaData [0] = (uint16) exif.fSubjectArea [0]; + fSubjectAreaData [1] = (uint16) exif.fSubjectArea [1]; + fSubjectAreaData [2] = (uint16) exif.fSubjectArea [2]; + fSubjectAreaData [3] = (uint16) exif.fSubjectArea [3]; + + fExifIFD.Add (&fSubjectArea); + + } + + if (exif.fLensInfo [0].IsValid () && + exif.fLensInfo [1].IsValid ()) + { + + fLensInfoData [0] = exif.fLensInfo [0]; + fLensInfoData [1] = exif.fLensInfo [1]; + fLensInfoData [2] = exif.fLensInfo [2]; + fLensInfoData [3] = exif.fLensInfo [3]; + + if (insideDNG) + { + directory.Add (&fLensInfo); + } + + } + + if (exif.fDateTime.IsValid ()) + { + + directory.Add (&fDateTime); + + if (exif.fDateTime.Subseconds ().NotEmpty ()) + { + fExifIFD.Add (&fSubsecTime); + } + + } + + if (exif.fDateTimeOriginal.IsValid ()) + { + + fExifIFD.Add (&fDateTimeOriginal); + + if (exif.fDateTimeOriginal.Subseconds ().NotEmpty ()) + { + fExifIFD.Add (&fSubsecTimeOriginal); + } + + } + + if (exif.fDateTimeDigitized.IsValid ()) + { + + fExifIFD.Add (&fDateTimeDigitized); + + if (exif.fDateTimeDigitized.Subseconds ().NotEmpty ()) + { + fExifIFD.Add (&fSubsecTimeDigitized); + } + + } + + if (exif.fMake.NotEmpty ()) + { + directory.Add (&fMake); + } + + if (exif.fModel.NotEmpty ()) + { + directory.Add (&fModel); + } + + if (exif.fArtist.NotEmpty ()) + { + directory.Add (&fArtist); + } + + if (exif.fSoftware.NotEmpty ()) + { + directory.Add (&fSoftware); + } + + if (exif.fCopyright.NotEmpty ()) + { + directory.Add (&fCopyright); + } + + if (exif.fImageDescription.NotEmpty ()) + { + directory.Add (&fImageDescription); + } + + if (exif.fCameraSerialNumber.NotEmpty () && insideDNG) + { + directory.Add (&fSerialNumber); + } + + if (makerNoteSafe && makerNoteData) + { + + directory.Add (&fMakerNoteSafety); + + fExifIFD.Add (&fMakerNote); + + } + + if (exif.fUserComment.NotEmpty ()) + { + fExifIFD.Add (&fUserComment); + } + + if (exif.fImageUniqueID.IsValid ()) + { + + for (uint32 j = 0; j < 16; j++) + { + + sprintf (fImageUniqueIDData + j * 2, + "%02X", + (unsigned) exif.fImageUniqueID.data [j]); + + } + + fExifIFD.Add (&fImageUniqueID); + + } + + if (exif.AtLeastVersion0230 ()) + { + + if (exif.fSensitivityType != 0) + { + + fExifIFD.Add (&fSensitivityType); + + } + + // Sensitivity tags. Do not write these extra tags unless the SensitivityType + // and PhotographicSensitivity (i.e., ISOSpeedRatings) values are valid. + + if (exif.fSensitivityType != 0 && + exif.fISOSpeedRatings [0] != 0) + { + + // Standard Output Sensitivity (SOS). + + if (exif.fStandardOutputSensitivity != 0) + { + fExifIFD.Add (&fStandardOutputSensitivity); + } + + // Recommended Exposure Index (REI). + + if (exif.fRecommendedExposureIndex != 0) + { + fExifIFD.Add (&fRecommendedExposureIndex); + } + + // ISO Speed. + + if (exif.fISOSpeed != 0) + { + + fExifIFD.Add (&fISOSpeed); + + if (exif.fISOSpeedLatitudeyyy != 0 && + exif.fISOSpeedLatitudezzz != 0) + { + + fExifIFD.Add (&fISOSpeedLatitudeyyy); + fExifIFD.Add (&fISOSpeedLatitudezzz); + + } + + } + + } + + if (exif.fOwnerName.NotEmpty ()) + { + fExifIFD.Add (&fCameraOwnerName); + } + + if (exif.fCameraSerialNumber.NotEmpty ()) + { + fExifIFD.Add (&fBodySerialNumber); + } + + if (exif.fLensInfo [0].IsValid () && + exif.fLensInfo [1].IsValid ()) + { + fExifIFD.Add (&fLensSpecification); + } + + if (exif.fLensMake.NotEmpty ()) + { + fExifIFD.Add (&fLensMake); + } + + if (exif.fLensName.NotEmpty ()) + { + fExifIFD.Add (&fLensModel); + } + + if (exif.fLensSerialNumber.NotEmpty ()) + { + fExifIFD.Add (&fLensSerialNumber); + } + + } + + if (exif.fGPSVersionID) + { + + fGPSVersionData [0] = (uint8) (exif.fGPSVersionID >> 24); + fGPSVersionData [1] = (uint8) (exif.fGPSVersionID >> 16); + fGPSVersionData [2] = (uint8) (exif.fGPSVersionID >> 8); + fGPSVersionData [3] = (uint8) (exif.fGPSVersionID ); + + fGPSIFD.Add (&fGPSVersionID); + + } + + if (exif.fGPSLatitudeRef.NotEmpty () && + exif.fGPSLatitude [0].IsValid ()) + { + fGPSIFD.Add (&fGPSLatitudeRef); + fGPSIFD.Add (&fGPSLatitude ); + } + + if (exif.fGPSLongitudeRef.NotEmpty () && + exif.fGPSLongitude [0].IsValid ()) + { + fGPSIFD.Add (&fGPSLongitudeRef); + fGPSIFD.Add (&fGPSLongitude ); + } + + if (exif.fGPSAltitudeRef <= 0x0FF) + { + fGPSIFD.Add (&fGPSAltitudeRef); + } + + if (exif.fGPSAltitude.IsValid ()) + { + fGPSIFD.Add (&fGPSAltitude); + } + + if (exif.fGPSTimeStamp [0].IsValid ()) + { + fGPSIFD.Add (&fGPSTimeStamp); + } + + if (exif.fGPSSatellites.NotEmpty ()) + { + fGPSIFD.Add (&fGPSSatellites); + } + + if (exif.fGPSStatus.NotEmpty ()) + { + fGPSIFD.Add (&fGPSStatus); + } + + if (exif.fGPSMeasureMode.NotEmpty ()) + { + fGPSIFD.Add (&fGPSMeasureMode); + } + + if (exif.fGPSDOP.IsValid ()) + { + fGPSIFD.Add (&fGPSDOP); + } + + if (exif.fGPSSpeedRef.NotEmpty ()) + { + fGPSIFD.Add (&fGPSSpeedRef); + } + + if (exif.fGPSSpeed.IsValid ()) + { + fGPSIFD.Add (&fGPSSpeed); + } + + if (exif.fGPSTrackRef.NotEmpty ()) + { + fGPSIFD.Add (&fGPSTrackRef); + } + + if (exif.fGPSTrack.IsValid ()) + { + fGPSIFD.Add (&fGPSTrack); + } + + if (exif.fGPSImgDirectionRef.NotEmpty ()) + { + fGPSIFD.Add (&fGPSImgDirectionRef); + } + + if (exif.fGPSImgDirection.IsValid ()) + { + fGPSIFD.Add (&fGPSImgDirection); + } + + if (exif.fGPSMapDatum.NotEmpty ()) + { + fGPSIFD.Add (&fGPSMapDatum); + } + + if (exif.fGPSDestLatitudeRef.NotEmpty () && + exif.fGPSDestLatitude [0].IsValid ()) + { + fGPSIFD.Add (&fGPSDestLatitudeRef); + fGPSIFD.Add (&fGPSDestLatitude ); + } + + if (exif.fGPSDestLongitudeRef.NotEmpty () && + exif.fGPSDestLongitude [0].IsValid ()) + { + fGPSIFD.Add (&fGPSDestLongitudeRef); + fGPSIFD.Add (&fGPSDestLongitude ); + } + + if (exif.fGPSDestBearingRef.NotEmpty ()) + { + fGPSIFD.Add (&fGPSDestBearingRef); + } + + if (exif.fGPSDestBearing.IsValid ()) + { + fGPSIFD.Add (&fGPSDestBearing); + } + + if (exif.fGPSDestDistanceRef.NotEmpty ()) + { + fGPSIFD.Add (&fGPSDestDistanceRef); + } + + if (exif.fGPSDestDistance.IsValid ()) + { + fGPSIFD.Add (&fGPSDestDistance); + } + + if (exif.fGPSProcessingMethod.NotEmpty ()) + { + fGPSIFD.Add (&fGPSProcessingMethod); + } + + if (exif.fGPSAreaInformation.NotEmpty ()) + { + fGPSIFD.Add (&fGPSAreaInformation); + } + + if (exif.fGPSDateStamp.NotEmpty ()) + { + fGPSIFD.Add (&fGPSDateStamp); + } + + if (exif.fGPSDifferential <= 0x0FFFF) + { + fGPSIFD.Add (&fGPSDifferential); + } + + if (exif.AtLeastVersion0230 ()) + { + + if (exif.fGPSHPositioningError.IsValid ()) + { + fGPSIFD.Add (&fGPSHPositioningError); + } + + } + + AddLinks (directory); + + } + +/******************************************************************************/ + +void exif_tag_set::AddLinks (dng_tiff_directory &directory) + { + + if (fExifIFD.Size () != 0 && !fAddedExifLink) + { + + directory.Add (&fExifLink); + + fAddedExifLink = true; + + } + + if (fGPSIFD.Size () != 0 && !fAddedGPSLink) + { + + directory.Add (&fGPSLink); + + fAddedGPSLink = true; + + } + + } + +/******************************************************************************/ + +class range_tag_set + { + + private: + + uint32 fActiveAreaData [4]; + + tag_uint32_ptr fActiveArea; + + uint32 fMaskedAreaData [kMaxMaskedAreas * 4]; + + tag_uint32_ptr fMaskedAreas; + + tag_uint16_ptr fLinearizationTable; + + uint16 fBlackLevelRepeatDimData [2]; + + tag_uint16_ptr fBlackLevelRepeatDim; + + dng_urational fBlackLevelData [kMaxBlackPattern * + kMaxBlackPattern * + kMaxSamplesPerPixel]; + + tag_urational_ptr fBlackLevel; + + dng_memory_data fBlackLevelDeltaHData; + dng_memory_data fBlackLevelDeltaVData; + + tag_srational_ptr fBlackLevelDeltaH; + tag_srational_ptr fBlackLevelDeltaV; + + uint16 fWhiteLevelData16 [kMaxSamplesPerPixel]; + uint32 fWhiteLevelData32 [kMaxSamplesPerPixel]; + + tag_uint16_ptr fWhiteLevel16; + tag_uint32_ptr fWhiteLevel32; + + public: + + range_tag_set (dng_tiff_directory &directory, + const dng_negative &negative); + + }; + +/******************************************************************************/ + +range_tag_set::range_tag_set (dng_tiff_directory &directory, + const dng_negative &negative) + + : fActiveArea (tcActiveArea, + fActiveAreaData, + 4) + + , fMaskedAreas (tcMaskedAreas, + fMaskedAreaData, + 0) + + , fLinearizationTable (tcLinearizationTable, + NULL, + 0) + + , fBlackLevelRepeatDim (tcBlackLevelRepeatDim, + fBlackLevelRepeatDimData, + 2) + + , fBlackLevel (tcBlackLevel, + fBlackLevelData) + + , fBlackLevelDeltaHData () + , fBlackLevelDeltaVData () + + , fBlackLevelDeltaH (tcBlackLevelDeltaH) + , fBlackLevelDeltaV (tcBlackLevelDeltaV) + + , fWhiteLevel16 (tcWhiteLevel, + fWhiteLevelData16) + + , fWhiteLevel32 (tcWhiteLevel, + fWhiteLevelData32) + + { + + const dng_image &rawImage (negative.RawImage ()); + + const dng_linearization_info *rangeInfo = negative.GetLinearizationInfo (); + + if (rangeInfo) + { + + // ActiveArea: + + { + + const dng_rect &r = rangeInfo->fActiveArea; + + if (r.NotEmpty ()) + { + + fActiveAreaData [0] = r.t; + fActiveAreaData [1] = r.l; + fActiveAreaData [2] = r.b; + fActiveAreaData [3] = r.r; + + directory.Add (&fActiveArea); + + } + + } + + // MaskedAreas: + + if (rangeInfo->fMaskedAreaCount) + { + + fMaskedAreas.SetCount (rangeInfo->fMaskedAreaCount * 4); + + for (uint32 index = 0; index < rangeInfo->fMaskedAreaCount; index++) + { + + const dng_rect &r = rangeInfo->fMaskedArea [index]; + + fMaskedAreaData [index * 4 + 0] = r.t; + fMaskedAreaData [index * 4 + 1] = r.l; + fMaskedAreaData [index * 4 + 2] = r.b; + fMaskedAreaData [index * 4 + 3] = r.r; + + } + + directory.Add (&fMaskedAreas); + + } + + // LinearizationTable: + + if (rangeInfo->fLinearizationTable.Get ()) + { + + fLinearizationTable.SetData (rangeInfo->fLinearizationTable->Buffer_uint16 () ); + fLinearizationTable.SetCount (rangeInfo->fLinearizationTable->LogicalSize () >> 1); + + directory.Add (&fLinearizationTable); + + } + + // BlackLevelRepeatDim: + + { + + fBlackLevelRepeatDimData [0] = (uint16) rangeInfo->fBlackLevelRepeatRows; + fBlackLevelRepeatDimData [1] = (uint16) rangeInfo->fBlackLevelRepeatCols; + + directory.Add (&fBlackLevelRepeatDim); + + } + + // BlackLevel: + + { + + uint32 index = 0; + + for (uint16 v = 0; v < rangeInfo->fBlackLevelRepeatRows; v++) + { + + for (uint32 h = 0; h < rangeInfo->fBlackLevelRepeatCols; h++) + { + + for (uint32 c = 0; c < rawImage.Planes (); c++) + { + + fBlackLevelData [index++] = rangeInfo->BlackLevel (v, h, c); + + } + + } + + } + + fBlackLevel.SetCount (rangeInfo->fBlackLevelRepeatRows * + rangeInfo->fBlackLevelRepeatCols * rawImage.Planes ()); + + directory.Add (&fBlackLevel); + + } + + // BlackLevelDeltaH: + + if (rangeInfo->ColumnBlackCount ()) + { + + uint32 count = rangeInfo->ColumnBlackCount (); + + fBlackLevelDeltaHData.Allocate (count * (uint32) sizeof (dng_srational)); + + dng_srational *blacks = (dng_srational *) fBlackLevelDeltaHData.Buffer (); + + for (uint32 col = 0; col < count; col++) + { + + blacks [col] = rangeInfo->ColumnBlack (col); + + } + + fBlackLevelDeltaH.SetData (blacks); + fBlackLevelDeltaH.SetCount (count ); + + directory.Add (&fBlackLevelDeltaH); + + } + + // BlackLevelDeltaV: + + if (rangeInfo->RowBlackCount ()) + { + + uint32 count = rangeInfo->RowBlackCount (); + + fBlackLevelDeltaVData.Allocate (count * (uint32) sizeof (dng_srational)); + + dng_srational *blacks = (dng_srational *) fBlackLevelDeltaVData.Buffer (); + + for (uint32 row = 0; row < count; row++) + { + + blacks [row] = rangeInfo->RowBlack (row); + + } + + fBlackLevelDeltaV.SetData (blacks); + fBlackLevelDeltaV.SetCount (count ); + + directory.Add (&fBlackLevelDeltaV); + + } + + } + + // WhiteLevel: + + // Only use the 32-bit data type if we must use it since there + // are some lazy (non-Adobe) DNG readers out there. + + bool needs32 = false; + + fWhiteLevel16.SetCount (rawImage.Planes ()); + fWhiteLevel32.SetCount (rawImage.Planes ()); + + for (uint32 c = 0; c < fWhiteLevel16.Count (); c++) + { + + fWhiteLevelData32 [c] = negative.WhiteLevel (c); + + if (fWhiteLevelData32 [c] > 0x0FFFF) + { + needs32 = true; + } + + fWhiteLevelData16 [c] = (uint16) fWhiteLevelData32 [c]; + + } + + if (needs32) + { + directory.Add (&fWhiteLevel32); + } + + else + { + directory.Add (&fWhiteLevel16); + } + + } + +/******************************************************************************/ + +class mosaic_tag_set + { + + private: + + uint16 fCFARepeatPatternDimData [2]; + + tag_uint16_ptr fCFARepeatPatternDim; + + uint8 fCFAPatternData [kMaxCFAPattern * + kMaxCFAPattern]; + + tag_uint8_ptr fCFAPattern; + + uint8 fCFAPlaneColorData [kMaxColorPlanes]; + + tag_uint8_ptr fCFAPlaneColor; + + tag_uint16 fCFALayout; + + tag_uint32 fGreenSplit; + + public: + + mosaic_tag_set (dng_tiff_directory &directory, + const dng_mosaic_info &info); + + }; + +/******************************************************************************/ + +mosaic_tag_set::mosaic_tag_set (dng_tiff_directory &directory, + const dng_mosaic_info &info) + + : fCFARepeatPatternDim (tcCFARepeatPatternDim, + fCFARepeatPatternDimData, + 2) + + , fCFAPattern (tcCFAPattern, + fCFAPatternData) + + , fCFAPlaneColor (tcCFAPlaneColor, + fCFAPlaneColorData) + + , fCFALayout (tcCFALayout, + (uint16) info.fCFALayout) + + , fGreenSplit (tcBayerGreenSplit, + info.fBayerGreenSplit) + + { + + if (info.IsColorFilterArray ()) + { + + // CFARepeatPatternDim: + + fCFARepeatPatternDimData [0] = (uint16) info.fCFAPatternSize.v; + fCFARepeatPatternDimData [1] = (uint16) info.fCFAPatternSize.h; + + directory.Add (&fCFARepeatPatternDim); + + // CFAPattern: + + fCFAPattern.SetCount (info.fCFAPatternSize.v * + info.fCFAPatternSize.h); + + for (int32 r = 0; r < info.fCFAPatternSize.v; r++) + { + + for (int32 c = 0; c < info.fCFAPatternSize.h; c++) + { + + fCFAPatternData [r * info.fCFAPatternSize.h + c] = info.fCFAPattern [r] [c]; + + } + + } + + directory.Add (&fCFAPattern); + + // CFAPlaneColor: + + fCFAPlaneColor.SetCount (info.fColorPlanes); + + for (uint32 j = 0; j < info.fColorPlanes; j++) + { + + fCFAPlaneColorData [j] = info.fCFAPlaneColor [j]; + + } + + directory.Add (&fCFAPlaneColor); + + // CFALayout: + + fCFALayout.Set ((uint16) info.fCFALayout); + + directory.Add (&fCFALayout); + + // BayerGreenSplit: (only include if the pattern is a Bayer pattern) + + if (info.fCFAPatternSize == dng_point (2, 2) && + info.fColorPlanes == 3) + { + + directory.Add (&fGreenSplit); + + } + + } + + } + +/******************************************************************************/ + +class color_tag_set + { + + private: + + uint32 fColorChannels; + + tag_matrix fCameraCalibration1; + tag_matrix fCameraCalibration2; + + tag_string fCameraCalibrationSignature; + + tag_string fAsShotProfileName; + + dng_urational fAnalogBalanceData [4]; + + tag_urational_ptr fAnalogBalance; + + dng_urational fAsShotNeutralData [4]; + + tag_urational_ptr fAsShotNeutral; + + dng_urational fAsShotWhiteXYData [2]; + + tag_urational_ptr fAsShotWhiteXY; + + tag_urational fLinearResponseLimit; + + public: + + color_tag_set (dng_tiff_directory &directory, + const dng_negative &negative); + + }; + +/******************************************************************************/ + +color_tag_set::color_tag_set (dng_tiff_directory &directory, + const dng_negative &negative) + + : fColorChannels (negative.ColorChannels ()) + + , fCameraCalibration1 (tcCameraCalibration1, + negative.CameraCalibration1 ()) + + , fCameraCalibration2 (tcCameraCalibration2, + negative.CameraCalibration2 ()) + + , fCameraCalibrationSignature (tcCameraCalibrationSignature, + negative.CameraCalibrationSignature ()) + + , fAsShotProfileName (tcAsShotProfileName, + negative.AsShotProfileName ()) + + , fAnalogBalance (tcAnalogBalance, + fAnalogBalanceData, + fColorChannels) + + , fAsShotNeutral (tcAsShotNeutral, + fAsShotNeutralData, + fColorChannels) + + , fAsShotWhiteXY (tcAsShotWhiteXY, + fAsShotWhiteXYData, + 2) + + , fLinearResponseLimit (tcLinearResponseLimit, + negative.LinearResponseLimitR ()) + + { + + if (fColorChannels > 1) + { + + uint32 channels2 = fColorChannels * fColorChannels; + + if (fCameraCalibration1.Count () == channels2) + { + + directory.Add (&fCameraCalibration1); + + } + + if (fCameraCalibration2.Count () == channels2) + { + + directory.Add (&fCameraCalibration2); + + } + + if (fCameraCalibration1.Count () == channels2 || + fCameraCalibration2.Count () == channels2) + { + + if (negative.CameraCalibrationSignature ().NotEmpty ()) + { + + directory.Add (&fCameraCalibrationSignature); + + } + + } + + if (negative.AsShotProfileName ().NotEmpty ()) + { + + directory.Add (&fAsShotProfileName); + + } + + for (uint32 j = 0; j < fColorChannels; j++) + { + + fAnalogBalanceData [j] = negative.AnalogBalanceR (j); + + } + + directory.Add (&fAnalogBalance); + + if (negative.HasCameraNeutral ()) + { + + for (uint32 k = 0; k < fColorChannels; k++) + { + + fAsShotNeutralData [k] = negative.CameraNeutralR (k); + + } + + directory.Add (&fAsShotNeutral); + + } + + else if (negative.HasCameraWhiteXY ()) + { + + negative.GetCameraWhiteXY (fAsShotWhiteXYData [0], + fAsShotWhiteXYData [1]); + + directory.Add (&fAsShotWhiteXY); + + } + + directory.Add (&fLinearResponseLimit); + + } + + } + +/******************************************************************************/ + +class profile_tag_set + { + + private: + + tag_uint16 fCalibrationIlluminant1; + tag_uint16 fCalibrationIlluminant2; + + tag_matrix fColorMatrix1; + tag_matrix fColorMatrix2; + + tag_matrix fForwardMatrix1; + tag_matrix fForwardMatrix2; + + tag_matrix fReductionMatrix1; + tag_matrix fReductionMatrix2; + + tag_string fProfileName; + + tag_string fProfileCalibrationSignature; + + tag_uint32 fEmbedPolicyTag; + + tag_string fCopyrightTag; + + uint32 fHueSatMapDimData [3]; + + tag_uint32_ptr fHueSatMapDims; + + tag_data_ptr fHueSatData1; + tag_data_ptr fHueSatData2; + + tag_uint32 fHueSatMapEncodingTag; + + uint32 fLookTableDimData [3]; + + tag_uint32_ptr fLookTableDims; + + tag_data_ptr fLookTableData; + + tag_uint32 fLookTableEncodingTag; + + tag_srational fBaselineExposureOffsetTag; + + tag_uint32 fDefaultBlackRenderTag; + + dng_memory_data fToneCurveBuffer; + + tag_data_ptr fToneCurveTag; + + public: + + profile_tag_set (dng_tiff_directory &directory, + const dng_camera_profile &profile); + + }; + +/******************************************************************************/ + +profile_tag_set::profile_tag_set (dng_tiff_directory &directory, + const dng_camera_profile &profile) + + : fCalibrationIlluminant1 (tcCalibrationIlluminant1, + (uint16) profile.CalibrationIlluminant1 ()) + + , fCalibrationIlluminant2 (tcCalibrationIlluminant2, + (uint16) profile.CalibrationIlluminant2 ()) + + , fColorMatrix1 (tcColorMatrix1, + profile.ColorMatrix1 ()) + + , fColorMatrix2 (tcColorMatrix2, + profile.ColorMatrix2 ()) + + , fForwardMatrix1 (tcForwardMatrix1, + profile.ForwardMatrix1 ()) + + , fForwardMatrix2 (tcForwardMatrix2, + profile.ForwardMatrix2 ()) + + , fReductionMatrix1 (tcReductionMatrix1, + profile.ReductionMatrix1 ()) + + , fReductionMatrix2 (tcReductionMatrix2, + profile.ReductionMatrix2 ()) + + , fProfileName (tcProfileName, + profile.Name (), + false) + + , fProfileCalibrationSignature (tcProfileCalibrationSignature, + profile.ProfileCalibrationSignature (), + false) + + , fEmbedPolicyTag (tcProfileEmbedPolicy, + profile.EmbedPolicy ()) + + , fCopyrightTag (tcProfileCopyright, + profile.Copyright (), + false) + + , fHueSatMapDims (tcProfileHueSatMapDims, + fHueSatMapDimData, + 3) + + , fHueSatData1 (tcProfileHueSatMapData1, + ttFloat, + profile.HueSatDeltas1 ().DeltasCount () * 3, + profile.HueSatDeltas1 ().GetConstDeltas ()) + + , fHueSatData2 (tcProfileHueSatMapData2, + ttFloat, + profile.HueSatDeltas2 ().DeltasCount () * 3, + profile.HueSatDeltas2 ().GetConstDeltas ()) + + , fHueSatMapEncodingTag (tcProfileHueSatMapEncoding, + profile.HueSatMapEncoding ()) + + , fLookTableDims (tcProfileLookTableDims, + fLookTableDimData, + 3) + + , fLookTableData (tcProfileLookTableData, + ttFloat, + profile.LookTable ().DeltasCount () * 3, + profile.LookTable ().GetConstDeltas ()) + + , fLookTableEncodingTag (tcProfileLookTableEncoding, + profile.LookTableEncoding ()) + + , fBaselineExposureOffsetTag (tcBaselineExposureOffset, + profile.BaselineExposureOffset ()) + + , fDefaultBlackRenderTag (tcDefaultBlackRender, + profile.DefaultBlackRender ()) + + , fToneCurveBuffer () + + , fToneCurveTag (tcProfileToneCurve, + ttFloat, + 0, + NULL) + + { + + if (profile.HasColorMatrix1 ()) + { + + uint32 colorChannels = profile.ColorMatrix1 ().Rows (); + + directory.Add (&fCalibrationIlluminant1); + + directory.Add (&fColorMatrix1); + + if (fForwardMatrix1.Count () == colorChannels * 3) + { + + directory.Add (&fForwardMatrix1); + + } + + if (colorChannels > 3 && fReductionMatrix1.Count () == colorChannels * 3) + { + + directory.Add (&fReductionMatrix1); + + } + + if (profile.HasColorMatrix2 ()) + { + + directory.Add (&fCalibrationIlluminant2); + + directory.Add (&fColorMatrix2); + + if (fForwardMatrix2.Count () == colorChannels * 3) + { + + directory.Add (&fForwardMatrix2); + + } + + if (colorChannels > 3 && fReductionMatrix2.Count () == colorChannels * 3) + { + + directory.Add (&fReductionMatrix2); + + } + + } + + if (profile.Name ().NotEmpty ()) + { + + directory.Add (&fProfileName); + + } + + if (profile.ProfileCalibrationSignature ().NotEmpty ()) + { + + directory.Add (&fProfileCalibrationSignature); + + } + + directory.Add (&fEmbedPolicyTag); + + if (profile.Copyright ().NotEmpty ()) + { + + directory.Add (&fCopyrightTag); + + } + + bool haveHueSat1 = profile.HueSatDeltas1 ().IsValid (); + + bool haveHueSat2 = profile.HueSatDeltas2 ().IsValid () && + profile.HasColorMatrix2 (); + + if (haveHueSat1 || haveHueSat2) + { + + uint32 hueDivs = 0; + uint32 satDivs = 0; + uint32 valDivs = 0; + + if (haveHueSat1) + { + + profile.HueSatDeltas1 ().GetDivisions (hueDivs, + satDivs, + valDivs); + + } + + else + { + + profile.HueSatDeltas2 ().GetDivisions (hueDivs, + satDivs, + valDivs); + + } + + fHueSatMapDimData [0] = hueDivs; + fHueSatMapDimData [1] = satDivs; + fHueSatMapDimData [2] = valDivs; + + directory.Add (&fHueSatMapDims); + + // Don't bother including the ProfileHueSatMapEncoding tag unless it's + // non-linear. + + if (profile.HueSatMapEncoding () != encoding_Linear) + { + + directory.Add (&fHueSatMapEncodingTag); + + } + + } + + if (haveHueSat1) + { + + directory.Add (&fHueSatData1); + + } + + if (haveHueSat2) + { + + directory.Add (&fHueSatData2); + + } + + if (profile.HasLookTable ()) + { + + uint32 hueDivs = 0; + uint32 satDivs = 0; + uint32 valDivs = 0; + + profile.LookTable ().GetDivisions (hueDivs, + satDivs, + valDivs); + + fLookTableDimData [0] = hueDivs; + fLookTableDimData [1] = satDivs; + fLookTableDimData [2] = valDivs; + + directory.Add (&fLookTableDims); + + directory.Add (&fLookTableData); + + // Don't bother including the ProfileLookTableEncoding tag unless it's + // non-linear. + + if (profile.LookTableEncoding () != encoding_Linear) + { + + directory.Add (&fLookTableEncodingTag); + + } + + } + + // Don't bother including the BaselineExposureOffset tag unless it's both + // valid and non-zero. + + if (profile.BaselineExposureOffset ().IsValid ()) + { + + if (profile.BaselineExposureOffset ().As_real64 () != 0.0) + { + + directory.Add (&fBaselineExposureOffsetTag); + + } + + } + + if (profile.DefaultBlackRender () != defaultBlackRender_Auto) + { + + directory.Add (&fDefaultBlackRenderTag); + + } + + if (profile.ToneCurve ().IsValid ()) + { + + // Tone curve stored as pairs of 32-bit coordinates. Probably could do with + // 16-bits here, but should be small number of points so... + + uint32 toneCurvePoints = (uint32) (profile.ToneCurve ().fCoord.size ()); + + fToneCurveBuffer.Allocate (toneCurvePoints * 2 * (uint32) sizeof (real32)); + + real32 *points = fToneCurveBuffer.Buffer_real32 (); + + fToneCurveTag.SetCount (toneCurvePoints * 2); + fToneCurveTag.SetData (points); + + for (uint32 i = 0; i < toneCurvePoints; i++) + { + + // Transpose coordinates so they are in a more expected + // order (domain -> range). + + points [i * 2 ] = (real32) profile.ToneCurve ().fCoord [i].h; + points [i * 2 + 1] = (real32) profile.ToneCurve ().fCoord [i].v; + + } + + directory.Add (&fToneCurveTag); + + } + + } + + } + +/******************************************************************************/ + +tiff_dng_extended_color_profile::tiff_dng_extended_color_profile + (const dng_camera_profile &profile) + + : fProfile (profile) + + { + + } + +/******************************************************************************/ + +void tiff_dng_extended_color_profile::Put (dng_stream &stream, + bool includeModelRestriction) + { + + // Profile header. + + stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII); + + stream.Put_uint16 (magicExtendedProfile); + + stream.Put_uint32 (8); + + // Profile tags. + + profile_tag_set tagSet (*this, fProfile); + + // Camera this profile is for. + + tag_string cameraModelTag (tcUniqueCameraModel, + fProfile.UniqueCameraModelRestriction ()); + + if (includeModelRestriction) + { + + if (fProfile.UniqueCameraModelRestriction ().NotEmpty ()) + { + + Add (&cameraModelTag); + + } + + } + + // Write it all out. + + dng_tiff_directory::Put (stream, offsetsRelativeToExplicitBase, 8); + + } + +/*****************************************************************************/ + +tag_dng_noise_profile::tag_dng_noise_profile (const dng_noise_profile &profile) + + : tag_data_ptr (tcNoiseProfile, + ttDouble, + 2 * profile.NumFunctions (), + fValues) + + { + + DNG_REQUIRE (profile.NumFunctions () <= kMaxColorPlanes, + "Too many noise functions in tag_dng_noise_profile."); + + for (uint32 i = 0; i < profile.NumFunctions (); i++) + { + + fValues [(2 * i) ] = profile.NoiseFunction (i).Scale (); + fValues [(2 * i) + 1] = profile.NoiseFunction (i).Offset (); + + } + + } + +/*****************************************************************************/ + +dng_image_writer::dng_image_writer () + { + fComputeMd5Sum = false; + } + +/*****************************************************************************/ + +dng_image_writer::~dng_image_writer () + { + + } + +/*****************************************************************************/ + +uint32 dng_image_writer::CompressedBufferSize (const dng_ifd &ifd, + uint32 uncompressedSize) + { + + switch (ifd.fCompression) + { + + case ccLZW: + { + + // Add lots of slop for LZW to expand data. + + return uncompressedSize * 2 + 1024; + + } + + case ccDeflate: + { + + // ZLib says maximum is source size + 0.1% + 12 bytes. + + return uncompressedSize + (uncompressedSize >> 8) + 64; + + } + + case ccJPEG: + { + + // If we are saving lossless JPEG from an 8-bit image, reserve + // space to pad the data out to 16-bits. + + if (ifd.fBitsPerSample [0] <= 8) + { + + return uncompressedSize * 2; + + } + + break; + + } + + default: + break; + + } + + return 0; + + } + +/******************************************************************************/ + +static void EncodeDelta8 (uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 channels) + { + + const uint32 dRowStep = cols * channels; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = cols - 1; col > 0; col--) + { + + for (uint32 channel = 0; channel < channels; channel++) + { + + dPtr [col * channels + channel] -= dPtr [(col - 1) * channels + channel]; + + } + + } + + dPtr += dRowStep; + + } + + } + +/******************************************************************************/ + +static void EncodeDelta16 (uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 channels) + { + + const uint32 dRowStep = cols * channels; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = cols - 1; col > 0; col--) + { + + for (uint32 channel = 0; channel < channels; channel++) + { + + dPtr [col * channels + channel] -= dPtr [(col - 1) * channels + channel]; + + } + + } + + dPtr += dRowStep; + + } + + } + +/******************************************************************************/ + +static void EncodeDelta32 (uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 channels) + { + + const uint32 dRowStep = cols * channels; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = cols - 1; col > 0; col--) + { + + for (uint32 channel = 0; channel < channels; channel++) + { + + dPtr [col * channels + channel] -= dPtr [(col - 1) * channels + channel]; + + } + + } + + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +inline void EncodeDeltaBytes (uint8 *bytePtr, int32 cols, int32 channels) + { + + if (channels == 1) + { + + bytePtr += (cols - 1); + + uint8 this0 = bytePtr [0]; + + for (int32 col = 1; col < cols; col++) + { + + uint8 prev0 = bytePtr [-1]; + + this0 -= prev0; + + bytePtr [0] = this0; + + this0 = prev0; + + bytePtr -= 1; + + } + + } + + else if (channels == 3) + { + + bytePtr += (cols - 1) * 3; + + uint8 this0 = bytePtr [0]; + uint8 this1 = bytePtr [1]; + uint8 this2 = bytePtr [2]; + + for (int32 col = 1; col < cols; col++) + { + + uint8 prev0 = bytePtr [-3]; + uint8 prev1 = bytePtr [-2]; + uint8 prev2 = bytePtr [-1]; + + this0 -= prev0; + this1 -= prev1; + this2 -= prev2; + + bytePtr [0] = this0; + bytePtr [1] = this1; + bytePtr [2] = this2; + + this0 = prev0; + this1 = prev1; + this2 = prev2; + + bytePtr -= 3; + + } + + } + + else + { + + uint32 rowBytes = cols * channels; + + bytePtr += rowBytes - 1; + + for (uint32 col = channels; col < rowBytes; col++) + { + + bytePtr [0] -= bytePtr [-channels]; + + bytePtr--; + + } + + } + + } + +/*****************************************************************************/ + +static void EncodeFPDelta (uint8 *buffer, + uint8 *temp, + int32 cols, + int32 channels, + int32 bytesPerSample) + { + + int32 rowIncrement = cols * channels; + + if (bytesPerSample == 2) + { + + const uint8 *src = buffer; + + #if qDNGBigEndian + uint8 *dst0 = temp; + uint8 *dst1 = temp + rowIncrement; + #else + uint8 *dst1 = temp; + uint8 *dst0 = temp + rowIncrement; + #endif + + for (int32 col = 0; col < rowIncrement; ++col) + { + + dst0 [col] = src [0]; + dst1 [col] = src [1]; + + src += 2; + + } + + } + + else if (bytesPerSample == 3) + { + + const uint8 *src = buffer; + + uint8 *dst0 = temp; + uint8 *dst1 = temp + rowIncrement; + uint8 *dst2 = temp + rowIncrement * 2; + + for (int32 col = 0; col < rowIncrement; ++col) + { + + dst0 [col] = src [0]; + dst1 [col] = src [1]; + dst2 [col] = src [2]; + + src += 3; + + } + + } + + else + { + + const uint8 *src = buffer; + + #if qDNGBigEndian + uint8 *dst0 = temp; + uint8 *dst1 = temp + rowIncrement; + uint8 *dst2 = temp + rowIncrement * 2; + uint8 *dst3 = temp + rowIncrement * 3; + #else + uint8 *dst3 = temp; + uint8 *dst2 = temp + rowIncrement; + uint8 *dst1 = temp + rowIncrement * 2; + uint8 *dst0 = temp + rowIncrement * 3; + #endif + + for (int32 col = 0; col < rowIncrement; ++col) + { + + dst0 [col] = src [0]; + dst1 [col] = src [1]; + dst2 [col] = src [2]; + dst3 [col] = src [3]; + + src += 4; + + } + + } + + EncodeDeltaBytes (temp, cols*bytesPerSample, channels); + + memcpy (buffer, temp, cols*bytesPerSample*channels); + + } + +/*****************************************************************************/ + +void dng_image_writer::EncodePredictor (dng_host &host, + const dng_ifd &ifd, + dng_pixel_buffer &buffer, + AutoPtr &tempBuffer) + { + + switch (ifd.fPredictor) + { + + case cpHorizontalDifference: + case cpHorizontalDifferenceX2: + case cpHorizontalDifferenceX4: + { + + int32 xFactor = 1; + + if (ifd.fPredictor == cpHorizontalDifferenceX2) + { + xFactor = 2; + } + + else if (ifd.fPredictor == cpHorizontalDifferenceX4) + { + xFactor = 4; + } + + switch (buffer.fPixelType) + { + + case ttByte: + { + + EncodeDelta8 ((uint8 *) buffer.fData, + buffer.fArea.H (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor); + + return; + + } + + case ttShort: + { + + EncodeDelta16 ((uint16 *) buffer.fData, + buffer.fArea.H (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor); + + return; + + } + + case ttLong: + { + + EncodeDelta32 ((uint32 *) buffer.fData, + buffer.fArea.H (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor); + + return; + + } + + default: + break; + + } + + break; + + } + + case cpFloatingPoint: + case cpFloatingPointX2: + case cpFloatingPointX4: + { + + int32 xFactor = 1; + + if (ifd.fPredictor == cpFloatingPointX2) + { + xFactor = 2; + } + + else if (ifd.fPredictor == cpFloatingPointX4) + { + xFactor = 4; + } + + uint32 tempBufferSize = buffer.fRowStep * buffer.fPixelSize; + + if (!tempBuffer.Get () || tempBuffer->LogicalSize () < tempBufferSize) + { + + tempBuffer.Reset (host.Allocate (tempBufferSize)); + + } + + for (int32 row = buffer.fArea.t; row < buffer.fArea.b; row++) + { + + EncodeFPDelta ((uint8 *) buffer.DirtyPixel (row, buffer.fArea.l, buffer.fPlane), + tempBuffer->Buffer_uint8 (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor, + buffer.fPixelSize); + + } + + return; + + } + + default: + break; + + } + + if (ifd.fPredictor != cpNullPredictor) + { + + ThrowProgramError (); + + } + + } + +/*****************************************************************************/ + +void dng_image_writer::ByteSwapBuffer (dng_host & /* host */, + dng_pixel_buffer &buffer) + { + + uint32 pixels = buffer.fRowStep * buffer.fArea.H (); + + switch (buffer.fPixelSize) + { + + case 2: + { + + DoSwapBytes16 ((uint16 *) buffer.fData, + pixels); + + break; + + } + + case 4: + { + + DoSwapBytes32 ((uint32 *) buffer.fData, + pixels); + + break; + + } + + default: + break; + + } + + } + +/*****************************************************************************/ + +void dng_image_writer::ReorderSubTileBlocks (const dng_ifd &ifd, + dng_pixel_buffer &buffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer) + { + + uint32 blockRows = ifd.fSubTileBlockRows; + uint32 blockCols = ifd.fSubTileBlockCols; + + uint32 rowBlocks = buffer.fArea.H () / blockRows; + uint32 colBlocks = buffer.fArea.W () / blockCols; + + int32 rowStep = buffer.fRowStep * buffer.fPixelSize; + int32 colStep = buffer.fColStep * buffer.fPixelSize; + + int32 rowBlockStep = rowStep * blockRows; + int32 colBlockStep = colStep * blockCols; + + uint32 blockColBytes = blockCols * buffer.fPlanes * buffer.fPixelSize; + + const uint8 *s0 = uncompressedBuffer->Buffer_uint8 (); + uint8 *d0 = subTileBlockBuffer->Buffer_uint8 (); + + for (uint32 rowBlock = 0; rowBlock < rowBlocks; rowBlock++) + { + + const uint8 *s1 = s0; + + for (uint32 colBlock = 0; colBlock < colBlocks; colBlock++) + { + + const uint8 *s2 = s1; + + for (uint32 blockRow = 0; blockRow < blockRows; blockRow++) + { + + for (uint32 j = 0; j < blockColBytes; j++) + { + + d0 [j] = s2 [j]; + + } + + d0 += blockColBytes; + + s2 += rowStep; + + } + + s1 += colBlockStep; + + } + + s0 += rowBlockStep; + + } + + // Copy back reordered pixels. + + DoCopyBytes (subTileBlockBuffer->Buffer (), + uncompressedBuffer->Buffer (), + uncompressedBuffer->LogicalSize ()); + + } + +/******************************************************************************/ + +class dng_lzw_compressor + { + + private: + + enum + { + kResetCode = 256, + kEndCode = 257, + kTableSize = 4096 + }; + + // Compressor nodes have two son pointers. The low order bit of + // the next code determines which pointer is used. This cuts the + // number of nodes searched for the next code by two on average. + + struct LZWCompressorNode + { + int16 final; + int16 son0; + int16 son1; + int16 brother; + }; + + dng_memory_data fBuffer; + + LZWCompressorNode *fTable; + + uint8 *fDstPtr; + + int32 fDstCount; + + int32 fBitOffset; + + int32 fNextCode; + + int32 fCodeSize; + + public: + + dng_lzw_compressor (); + + void Compress (const uint8 *sPtr, + uint8 *dPtr, + uint32 sCount, + uint32 &dCount); + + private: + + void InitTable (); + + int32 SearchTable (int32 w, int32 k) const + { + + DNG_ASSERT ((w >= 0) && (w <= kTableSize), + "Bad w value in dng_lzw_compressor::SearchTable"); + + int32 son0 = fTable [w] . son0; + int32 son1 = fTable [w] . son1; + + // Branchless version of: + // int32 code = (k & 1) ? son1 : son0; + + int32 code = son0 + ((-((int32) (k & 1))) & (son1 - son0)); + + while (code > 0 && fTable [code].final != k) + { + code = fTable [code].brother; + } + + return code; + + } + + void AddTable (int32 w, int32 k); + + void PutCodeWord (int32 code); + + // Hidden copy constructor and assignment operator. + + dng_lzw_compressor (const dng_lzw_compressor &compressor); + + dng_lzw_compressor & operator= (const dng_lzw_compressor &compressor); + + }; + +/******************************************************************************/ + +dng_lzw_compressor::dng_lzw_compressor () + + : fBuffer () + , fTable (NULL) + , fDstPtr (NULL) + , fDstCount (0) + , fBitOffset (0) + , fNextCode (0) + , fCodeSize (0) + + { + + fBuffer.Allocate (kTableSize * sizeof (LZWCompressorNode)); + + fTable = (LZWCompressorNode *) fBuffer.Buffer (); + + } + +/******************************************************************************/ + +void dng_lzw_compressor::InitTable () + { + + fCodeSize = 9; + + fNextCode = 258; + + LZWCompressorNode *node = &fTable [0]; + + for (int32 code = 0; code < 256; ++code) + { + + node->final = (int16) code; + node->son0 = -1; + node->son1 = -1; + node->brother = -1; + + node++; + + } + + } + +/******************************************************************************/ + +void dng_lzw_compressor::AddTable (int32 w, int32 k) + { + + DNG_ASSERT ((w >= 0) && (w <= kTableSize), + "Bad w value in dng_lzw_compressor::AddTable"); + + LZWCompressorNode *node = &fTable [w]; + + int32 nextCode = fNextCode; + + DNG_ASSERT ((nextCode >= 0) && (nextCode <= kTableSize), + "Bad fNextCode value in dng_lzw_compressor::AddTable"); + + LZWCompressorNode *node2 = &fTable [nextCode]; + + fNextCode++; + + int32 oldSon; + + if( k&1 ) + { + oldSon = node->son1; + node->son1 = (int16) nextCode; + } + else + { + oldSon = node->son0; + node->son0 = (int16) nextCode; + } + + node2->final = (int16) k; + node2->son0 = -1; + node2->son1 = -1; + node2->brother = (int16) oldSon; + + if (nextCode == (1 << fCodeSize) - 1) + { + if (fCodeSize != 12) + fCodeSize++; + } + + } + +/******************************************************************************/ + +void dng_lzw_compressor::PutCodeWord (int32 code) + { + + int32 bit = (int32) (fBitOffset & 7); + + int32 offset1 = fBitOffset >> 3; + int32 offset2 = (fBitOffset + fCodeSize - 1) >> 3; + + int32 shift1 = (fCodeSize + bit) - 8; + int32 shift2 = (fCodeSize + bit) - 16; + + uint8 byte1 = (uint8) (code >> shift1); + + uint8 *dstPtr1 = fDstPtr + offset1; + uint8 *dstPtr3 = fDstPtr + offset2; + + if (offset1 + 1 == offset2) + { + + uint8 byte2 = (uint8) (code << (-shift2)); + + if (bit) + *dstPtr1 |= byte1; + else + *dstPtr1 = byte1; + + *dstPtr3 = byte2; + + } + + else + { + + int32 shift3 = (fCodeSize + bit) - 24; + + uint8 byte2 = (uint8) (code >> shift2); + uint8 byte3 = (uint8) (code << (-shift3)); + + uint8 *dstPtr2 = fDstPtr + (offset1 + 1); + + if (bit) + *dstPtr1 |= byte1; + else + *dstPtr1 = byte1; + + *dstPtr2 = byte2; + + *dstPtr3 = byte3; + + } + + fBitOffset += fCodeSize; + + } + +/******************************************************************************/ + +void dng_lzw_compressor::Compress (const uint8 *sPtr, + uint8 *dPtr, + uint32 sCount, + uint32 &dCount) + { + + fDstPtr = dPtr; + + fBitOffset = 0; + + InitTable (); + + PutCodeWord (kResetCode); + + int32 code = -1; + + int32 pixel; + + if (sCount > 0) + { + + pixel = *sPtr; + sPtr = sPtr + 1; + code = pixel; + + sCount--; + + while (sCount--) + { + + pixel = *sPtr; + sPtr = sPtr + 1; + + int32 newCode = SearchTable (code, pixel); + + if (newCode == -1) + { + + PutCodeWord (code); + + if (fNextCode < 4093) + { + AddTable (code, pixel); + } + else + { + PutCodeWord (kResetCode); + InitTable (); + } + + code = pixel; + + } + + else + code = newCode; + + } + + } + + if (code != -1) + { + PutCodeWord (code); + AddTable (code, 0); + } + + PutCodeWord (kEndCode); + + dCount = (fBitOffset + 7) >> 3; + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteData (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_pixel_buffer &buffer, + AutoPtr &compressedBuffer) + { + + switch (ifd.fCompression) + { + + case ccUncompressed: + { + + // Special case support for when we save to 8-bits from + // 16-bit data. + + if (ifd.fBitsPerSample [0] == 8 && buffer.fPixelType == ttShort) + { + + uint32 count = buffer.fRowStep * + buffer.fArea.H (); + + const uint16 *sPtr = (const uint16 *) buffer.fData; + + for (uint32 j = 0; j < count; j++) + { + + stream.Put_uint8 ((uint8) sPtr [j]); + + } + + } + + else + { + + // Swap bytes if required. + + if (stream.SwapBytes ()) + { + + ByteSwapBuffer (host, buffer); + + } + + // Write the bytes. + + stream.Put (buffer.fData, buffer.fRowStep * + buffer.fArea.H () * + buffer.fPixelSize); + + } + + break; + + } + + case ccLZW: + case ccDeflate: + { + // These compression types are not supported + ThrowProgramError(); + break; + + } + + case ccJPEG: + { + + dng_pixel_buffer temp (buffer); + + if (buffer.fPixelType == ttByte) + { + + // The lossless JPEG encoder needs 16-bit data, so if we are + // are saving 8 bit data, we need to pad it out to 16-bits. + + temp.fData = compressedBuffer->Buffer (); + + temp.fPixelType = ttShort; + temp.fPixelSize = 2; + + temp.CopyArea (buffer, + buffer.fArea, + buffer.fPlane, + buffer.fPlanes); + + } + + EncodeLosslessJPEG ((const uint16 *) temp.fData, + temp.fArea.H (), + temp.fArea.W (), + temp.fPlanes, + ifd.fBitsPerSample [0], + temp.fRowStep, + temp.fColStep, + stream); + + break; + + } + + default: + { + + ThrowProgramError (); + + } + + } + + } + +/******************************************************************************/ + +void dng_image_writer::EncodeJPEGPreview (dng_host &host, + const dng_image &image, + dng_jpeg_preview &preview, + int32 quality) + { + + (void) host; + (void) image; + (void) preview; + (void) quality; + + ThrowProgramError ("No JPEG encoder"); + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteTile (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + const dng_image &image, + const dng_rect &tileArea, + uint32 fakeChannels, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer, + AutoPtr &tempBuffer) + { + + // Create pixel buffer to hold uncompressed tile. + + dng_pixel_buffer buffer; + + buffer.fArea = tileArea; + + buffer.fPlane = 0; + buffer.fPlanes = ifd.fSamplesPerPixel; + + buffer.fRowStep = buffer.fPlanes * tileArea.W (); + buffer.fColStep = buffer.fPlanes; + buffer.fPlaneStep = 1; + + buffer.fPixelType = image.PixelType (); + buffer.fPixelSize = image.PixelSize (); + + buffer.fData = uncompressedBuffer->Buffer (); + + // Get the uncompressed data. + + image.Get (buffer, dng_image::edge_zero); + + // Deal with sub-tile blocks. + + if (ifd.fSubTileBlockRows > 1) + { + + ReorderSubTileBlocks (ifd, + buffer, + uncompressedBuffer, + subTileBlockBuffer); + + } + + // Floating point depth conversion. + + if (ifd.fSampleFormat [0] == sfFloatingPoint) + { + + if (ifd.fBitsPerSample [0] == 16) + { + + uint32 *srcPtr = (uint32 *) buffer.fData; + uint16 *dstPtr = (uint16 *) buffer.fData; + + uint32 pixels = tileArea.W () * tileArea.H () * buffer.fPlanes; + + for (uint32 j = 0; j < pixels; j++) + { + + dstPtr [j] = DNG_FloatToHalf (srcPtr [j]); + + } + + buffer.fPixelSize = 2; + + } + + if (ifd.fBitsPerSample [0] == 24) + { + + uint32 *srcPtr = (uint32 *) buffer.fData; + uint8 *dstPtr = (uint8 *) buffer.fData; + + uint32 pixels = tileArea.W () * tileArea.H () * buffer.fPlanes; + + if (stream.BigEndian () || ifd.fPredictor == cpFloatingPoint || + ifd.fPredictor == cpFloatingPointX2 || + ifd.fPredictor == cpFloatingPointX4) + { + + for (uint32 j = 0; j < pixels; j++) + { + + DNG_FloatToFP24 (srcPtr [j], dstPtr); + + dstPtr += 3; + + } + + } + + else + { + + for (uint32 j = 0; j < pixels; j++) + { + + uint8 output [3]; + + DNG_FloatToFP24 (srcPtr [j], output); + + dstPtr [0] = output [2]; + dstPtr [1] = output [1]; + dstPtr [2] = output [0]; + + dstPtr += 3; + + } + + } + + buffer.fPixelSize = 3; + + } + + } + + // Run predictor. + + EncodePredictor (host, + ifd, + buffer, + tempBuffer); + + // Adjust pixel buffer for fake channels. + + if (fakeChannels > 1) + { + + buffer.fPlanes *= fakeChannels; + buffer.fColStep *= fakeChannels; + + buffer.fArea.r = buffer.fArea.l + (buffer.fArea.W () / fakeChannels); + + } + + // Compress (if required) and write out the data. + + WriteData (host, + ifd, + stream, + buffer, + compressedBuffer); + + } + +/*****************************************************************************/ + +class dng_write_tiles_task : public dng_area_task + { + + private: + + dng_image_writer &fImageWriter; + + dng_host &fHost; + + const dng_ifd &fIFD; + + dng_basic_tag_set &fBasic; + + dng_stream &fStream; + + const dng_image &fImage; + + uint32 fFakeChannels; + + uint32 fTilesDown; + + uint32 fTilesAcross; + + uint32 fCompressedSize; + + uint32 fUncompressedSize; + + dng_mutex fMutex1; + + uint32 fNextTileIndex; + + dng_mutex fMutex2; + +#if qDNGThreadSafe + dng_condition fCondition; +#endif + + bool fTaskFailed; + + uint32 fWriteTileIndex; + + public: + + dng_write_tiles_task (dng_image_writer &imageWriter, + dng_host &host, + const dng_ifd &ifd, + dng_basic_tag_set &basic, + dng_stream &stream, + const dng_image &image, + uint32 fakeChannels, + uint32 tilesDown, + uint32 tilesAcross, + uint32 compressedSize, + uint32 uncompressedSize) + + : fImageWriter (imageWriter) + , fHost (host) + , fIFD (ifd) + , fBasic (basic) + , fStream (stream) + , fImage (image) + , fFakeChannels (fakeChannels) + , fTilesDown (tilesDown) + , fTilesAcross (tilesAcross) + , fCompressedSize (compressedSize) + , fUncompressedSize (uncompressedSize) + , fMutex1 ("dng_write_tiles_task_1") + , fNextTileIndex (0) + , fMutex2 ("dng_write_tiles_task_2") +#if qDNGThreadSafe + , fCondition () +#endif + , fTaskFailed (false) + , fWriteTileIndex (0) + + { + + fMinTaskArea = 16 * 16; + fUnitCell = dng_point (16, 16); + fMaxTileSize = dng_point (16, 16); + + } + + void Process (uint32 /* threadIndex */, + const dng_rect & /* tile */, + dng_abort_sniffer *sniffer) + { + + try + { + + AutoPtr compressedBuffer; + AutoPtr uncompressedBuffer; + AutoPtr subTileBlockBuffer; + AutoPtr tempBuffer; + + if (fCompressedSize) + { + compressedBuffer.Reset (fHost.Allocate (fCompressedSize)); + } + + if (fUncompressedSize) + { + uncompressedBuffer.Reset (fHost.Allocate (fUncompressedSize)); + } + + if (fIFD.fSubTileBlockRows > 1 && fUncompressedSize) + { + subTileBlockBuffer.Reset (fHost.Allocate (fUncompressedSize)); + } + + while (true) + { + + // Find tile index to compress. + + uint32 tileIndex; + + { + + dng_lock_mutex lock (&fMutex1); + + if (fNextTileIndex == fTilesDown * fTilesAcross) + { + return; + } + + tileIndex = fNextTileIndex++; + + } + + dng_abort_sniffer::SniffForAbort (sniffer); + + // Compress tile. + + uint32 rowIndex = tileIndex / fTilesAcross; + + uint32 colIndex = tileIndex - rowIndex * fTilesAcross; + + dng_rect tileArea = fIFD.TileArea (rowIndex, colIndex); + + dng_memory_stream tileStream (fHost.Allocator ()); + + tileStream.SetLittleEndian (fStream.LittleEndian ()); + + dng_host host (&fHost.Allocator (), + sniffer); + + fImageWriter.WriteTile (host, + fIFD, + tileStream, + fImage, + tileArea, + fFakeChannels, + compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer, + tempBuffer); + + tileStream.Flush (); + + uint32 tileByteCount = (uint32) tileStream.Length (); + + tileStream.SetReadPosition (0); + + // Wait until it is our turn to write tile. + + { + + dng_lock_mutex lock (&fMutex2); + + while (!fTaskFailed && + fWriteTileIndex != tileIndex) + { + +#if qDNGThreadSafe + fCondition.Wait (fMutex2); +#endif + + } + + // If the task failed in another thread, that thread already threw an exception. + + if (fTaskFailed) + return; + + } + + dng_abort_sniffer::SniffForAbort (sniffer); + + // Remember this offset. + + uint32 tileOffset = (uint32) fStream.Position (); + + fBasic.SetTileOffset (tileIndex, tileOffset); + + // Copy tile stream for tile into main stream. + + tileStream.CopyToStream (fStream, tileByteCount); + + // Update tile count. + + fBasic.SetTileByteCount (tileIndex, tileByteCount); + + // Keep the tiles on even byte offsets. + + if (tileByteCount & 1) + { + fStream.Put_uint8 (0); + } + + // Let other threads know it is safe to write to stream. + + { + + dng_lock_mutex lock (&fMutex2); + + // If the task failed in another thread, that thread already threw an exception. + + if (fTaskFailed) + return; + + fWriteTileIndex++; + +#if qDNGThreadSafe + fCondition.Broadcast (); +#endif + + } + + } + + } + + catch (...) + { + + // If first to fail, wake up any threads waiting on condition. + + bool needBroadcast = false; + + { + + dng_lock_mutex lock (&fMutex2); + + needBroadcast = !fTaskFailed; + fTaskFailed = true; + + } + +#if qDNGThreadSafe + if (needBroadcast) + fCondition.Broadcast (); +#endif + + throw; + + } + + } + + private: + + // Hidden copy constructor and assignment operator. + + dng_write_tiles_task (const dng_write_tiles_task &); + + dng_write_tiles_task & operator= (const dng_write_tiles_task &); + + }; + +/*****************************************************************************/ + +void dng_image_writer::WriteImage (dng_host &host, + const dng_ifd &ifd, + dng_basic_tag_set &basic, + dng_stream &stream, + const dng_image &image, + uint32 fakeChannels) + { + + // Deal with row interleaved images. + + if (ifd.fRowInterleaveFactor > 1 && + ifd.fRowInterleaveFactor < ifd.fImageLength) + { + + dng_ifd tempIFD (ifd); + + tempIFD.fRowInterleaveFactor = 1; + + dng_row_interleaved_image tempImage (*((dng_image *) &image), + ifd.fRowInterleaveFactor); + + WriteImage (host, + tempIFD, + basic, + stream, + tempImage, + fakeChannels); + + return; + + } + + // Compute basic information. + + uint32 bytesPerSample = TagTypeSize (image.PixelType ()); + + uint32 bytesPerPixel = ifd.fSamplesPerPixel * bytesPerSample; + + uint32 tileRowBytes = ifd.fTileWidth * bytesPerPixel; + + // If we can compute the number of bytes needed to store the + // data, we can split the write for each tile into sub-tiles. + + uint32 subTileLength = ifd.fTileLength; + + if (ifd.TileByteCount (ifd.TileArea (0, 0)) != 0) + { + + subTileLength = Pin_uint32 (ifd.fSubTileBlockRows, + kImageBufferSize / tileRowBytes, + ifd.fTileLength); + + // Don't split sub-tiles across subTileBlocks. + + subTileLength = subTileLength / ifd.fSubTileBlockRows + * ifd.fSubTileBlockRows; + + } + + // Find size of uncompressed buffer. + + uint32 uncompressedSize = subTileLength * tileRowBytes; + + // Find size of compressed buffer, if required. + + uint32 compressedSize = CompressedBufferSize (ifd, uncompressedSize); + // See if we can do this write using multiple threads. + + uint32 tilesAcross = ifd.TilesAcross (); + uint32 tilesDown = ifd.TilesDown (); + + bool useMultipleThreads = (tilesDown * tilesAcross >= 2) && + (host.PerformAreaTaskThreads () > 1) && + (subTileLength == ifd.fTileLength) && + (ifd.fCompression != ccUncompressed); + + +#if qImagecore + useMultipleThreads = false; +#endif + + if (useMultipleThreads) + { + + uint32 threadCount = Min_uint32 (tilesDown * tilesAcross, + host.PerformAreaTaskThreads ()); + + dng_write_tiles_task task (*this, + host, + ifd, + basic, + stream, + image, + fakeChannels, + tilesDown, + tilesAcross, + compressedSize, + uncompressedSize); + + host.PerformAreaTask (task, + dng_rect (0, 0, 16, 16 * threadCount)); + + } + + else + { + AutoPtr compressedBuffer; + AutoPtr uncompressedBuffer; + AutoPtr subTileBlockBuffer; + AutoPtr tempBuffer; + + if (compressedSize) + { + compressedBuffer.Reset (host.Allocate (compressedSize)); + } + + if (uncompressedSize +#if GPR_WRITING + && ifd.fCompression != ccVc5 +#endif + ) + { + uncompressedBuffer.Reset (host.Allocate (uncompressedSize)); + } + + if (ifd.fSubTileBlockRows > 1 && uncompressedSize) + { + subTileBlockBuffer.Reset (host.Allocate (uncompressedSize)); + } + + // Write out each tile. + uint32 tileIndex = 0; + + for (uint32 rowIndex = 0; rowIndex < tilesDown; rowIndex++) + { + + for (uint32 colIndex = 0; colIndex < tilesAcross; colIndex++) + { + + // Remember this offset. + + uint32 tileOffset = (uint32) stream.Position (); + + basic.SetTileOffset (tileIndex, tileOffset); + + // Split tile into sub-tiles if possible. + + dng_rect tileArea = ifd.TileArea (rowIndex, colIndex); + + uint32 subTileCount = (tileArea.H () + subTileLength - 1) / + subTileLength; + + for (uint32 subIndex = 0; subIndex < subTileCount; subIndex++) + { + + host.SniffForAbort (); + + dng_rect subArea (tileArea); + + subArea.t = tileArea.t + subIndex * subTileLength; + + subArea.b = Min_int32 (subArea.t + subTileLength, + tileArea.b); + + // Write the sub-tile. + + WriteTile (host, + ifd, + stream, + image, + subArea, + fakeChannels, + compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer, + tempBuffer); + + } + + // Update tile count. + + uint32 tileByteCount = (uint32) stream.Position () - tileOffset; + + basic.SetTileByteCount (tileIndex, tileByteCount); + + tileIndex++; + + // Keep the tiles on even byte offsets. + + if (tileByteCount & 1) + { + stream.Put_uint8 (0); + } + + } + + } + } + + } + +/*****************************************************************************/ + +static void CopyString (const dng_xmp &oldXMP, + dng_xmp &newXMP, + const char *ns, + const char *path, + dng_string *exif = NULL) + { + + dng_string s; + + if (oldXMP.GetString (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + newXMP.SetString (ns, path, s); + + if (exif) + { + + *exif = s; + + } + + } + + } + + } + +/*****************************************************************************/ + +static void CopyStringList (const dng_xmp &oldXMP, + dng_xmp &newXMP, + const char *ns, + const char *path, + bool isBag) + { + + dng_string_list list; + + if (oldXMP.GetStringList (ns, path, list)) + { + + if (list.Count ()) + { + + newXMP.SetStringList (ns, path, list, isBag); + + } + + } + + } + +/*****************************************************************************/ + +static void CopyAltLangDefault (const dng_xmp &oldXMP, + dng_xmp &newXMP, + const char *ns, + const char *path, + dng_string *exif = NULL) + { + + dng_string s; + + if (oldXMP.GetAltLangDefault (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + newXMP.SetAltLangDefault (ns, path, s); + + if (exif) + { + + *exif = s; + + } + + } + + } + + } + +/*****************************************************************************/ + +static void CopyStructField (const dng_xmp &oldXMP, + dng_xmp &newXMP, + const char *ns, + const char *path, + const char *field) + { + + dng_string s; + + if (oldXMP.GetStructField (ns, path, ns, field, s)) + { + + if (s.NotEmpty ()) + { + + newXMP.SetStructField (ns, path, ns, field, s); + + } + + } + + } + +/*****************************************************************************/ + +static void CopyBoolean (const dng_xmp &oldXMP, + dng_xmp &newXMP, + const char *ns, + const char *path) + { + + bool b; + + if (oldXMP.GetBoolean (ns, path, b)) + { + + newXMP.SetBoolean (ns, path, b); + + } + + } + +/*****************************************************************************/ + +void dng_image_writer::CleanUpMetadata (dng_host &host, + dng_metadata &metadata, + dng_metadata_subset metadataSubset, + const char *dstMIMI, + const char *software) + { + + if (metadata.GetXMP () && metadata.GetExif ()) + { + + dng_xmp &newXMP (*metadata.GetXMP ()); + dng_exif &newEXIF (*metadata.GetExif ()); + + // Update software tag. + + if (software) + { + + newEXIF.fSoftware.Set (software); + + newXMP.Set (XMP_NS_XAP, + "CreatorTool", + software); + + } + + #if qDNGXMPDocOps + + newXMP.DocOpsPrepareForSave (metadata.SourceMIMI ().Get (), + dstMIMI); + + #else + + metadata.UpdateDateTimeToNow (); + + #endif + + // Update EXIF version to at least 2.3 so all the exif tags + // can be written. + + if (newEXIF.fExifVersion < DNG_CHAR4 ('0','2','3','0')) + { + + newEXIF.fExifVersion = DNG_CHAR4 ('0','2','3','0'); + + newXMP.Set (XMP_NS_EXIF, "ExifVersion", "0230"); + + } + + // Resync EXIF, remove EXIF tags from XMP. + + newXMP.SyncExif (newEXIF, + metadata.GetOriginalExif (), + false, + true); + + // Deal with ImageIngesterPro bug. This program is adding lots of + // empty metadata strings into the XMP, which is screwing up Adobe CS4. + // We are saving a new file, so this is a chance to clean up this mess. + + newXMP.RemoveEmptyStringsAndArrays (XMP_NS_DC); + newXMP.RemoveEmptyStringsAndArrays (XMP_NS_XAP); + newXMP.RemoveEmptyStringsAndArrays (XMP_NS_PHOTOSHOP); + newXMP.RemoveEmptyStringsAndArrays (XMP_NS_IPTC); + newXMP.RemoveEmptyStringsAndArrays (XMP_NS_XAP_RIGHTS); + newXMP.RemoveEmptyStringsAndArrays ("http://ns.iview-multimedia.com/mediapro/1.0/"); + + // Process metadata subset. + + if (metadataSubset == kMetadataSubset_CopyrightOnly || + metadataSubset == kMetadataSubset_CopyrightAndContact) + { + + dng_xmp oldXMP (newXMP ); + dng_exif oldEXIF (newEXIF); + + // For these options, we start from nothing, and only fill in the + // fields that we absolutely need. + + newXMP.RemoveProperties (NULL); + + newEXIF.SetEmpty (); + + metadata.ClearMakerNote (); + + // Move copyright related fields over. + + CopyAltLangDefault (oldXMP, + newXMP, + XMP_NS_DC, + "rights", + &newEXIF.fCopyright); + + CopyAltLangDefault (oldXMP, + newXMP, + XMP_NS_XAP_RIGHTS, + "UsageTerms"); + + CopyString (oldXMP, + newXMP, + XMP_NS_XAP_RIGHTS, + "WebStatement"); + + CopyBoolean (oldXMP, + newXMP, + XMP_NS_XAP_RIGHTS, + "Marked"); + + #if qDNGXMPDocOps + + // Include basic DocOps fields, but not the full history. + + CopyString (oldXMP, + newXMP, + XMP_NS_MM, + "OriginalDocumentID"); + + CopyString (oldXMP, + newXMP, + XMP_NS_MM, + "DocumentID"); + + CopyString (oldXMP, + newXMP, + XMP_NS_MM, + "InstanceID"); + + CopyString (oldXMP, + newXMP, + XMP_NS_XAP, + "MetadataDate"); + + #endif + + // Copyright and Contact adds the contact info fields. + + if (metadataSubset == kMetadataSubset_CopyrightAndContact) + { + + // Note: Save for Web is not including the dc:creator list, but it + // is part of the IPTC contract info metadata panel, so I + // think it should be copied as part of the contact info. + + CopyStringList (oldXMP, + newXMP, + XMP_NS_DC, + "creator", + false); + + // The first string dc:creator list is mirrored to the + // the exif artist tag, so copy that also. + + newEXIF.fArtist = oldEXIF.fArtist; + + // Copy other contact fields. + + CopyString (oldXMP, + newXMP, + XMP_NS_PHOTOSHOP, + "AuthorsPosition"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiEmailWork"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiAdrExtadr"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiAdrCity"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiAdrRegion"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiAdrPcode"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiAdrCtry"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiTelWork"); + + CopyStructField (oldXMP, + newXMP, + XMP_NS_IPTC, + "CreatorContactInfo", + "CiUrlWork"); + + CopyAltLangDefault (oldXMP, + newXMP, + XMP_NS_DC, + "title"); + + } + + } + + else if (metadataSubset == kMetadataSubset_AllExceptCameraInfo || + metadataSubset == kMetadataSubset_AllExceptCameraAndLocation || + metadataSubset == kMetadataSubset_AllExceptLocationInfo) + { + + dng_xmp oldXMP (newXMP ); + dng_exif oldEXIF (newEXIF); + + if (metadataSubset == kMetadataSubset_AllExceptCameraInfo || + metadataSubset == kMetadataSubset_AllExceptCameraAndLocation) + { + + // This removes most of the EXIF info, so just copy the fields + // we are not deleting. + + newEXIF.SetEmpty (); + + newEXIF.fImageDescription = oldEXIF.fImageDescription; // Note: Differs from SFW + newEXIF.fSoftware = oldEXIF.fSoftware; + newEXIF.fArtist = oldEXIF.fArtist; + newEXIF.fCopyright = oldEXIF.fCopyright; + newEXIF.fCopyright2 = oldEXIF.fCopyright2; + newEXIF.fDateTime = oldEXIF.fDateTime; + newEXIF.fDateTimeOriginal = oldEXIF.fDateTimeOriginal; + newEXIF.fDateTimeDigitized = oldEXIF.fDateTimeDigitized; + newEXIF.fExifVersion = oldEXIF.fExifVersion; + newEXIF.fImageUniqueID = oldEXIF.fImageUniqueID; + + newEXIF.CopyGPSFrom (oldEXIF); + + // Remove exif info from XMP. + + newXMP.RemoveProperties (XMP_NS_EXIF); + newXMP.RemoveProperties (XMP_NS_AUX); + + // Remove Camera Raw info + + newXMP.RemoveProperties (XMP_NS_CRS); + newXMP.RemoveProperties (XMP_NS_CRSS); + newXMP.RemoveProperties (XMP_NS_CRX); + + // Remove DocOps history, since it contains the original + // camera format. + + newXMP.Remove (XMP_NS_MM, "History"); + + // MakerNote contains camera info. + + metadata.ClearMakerNote (); + + } + + if (metadataSubset == kMetadataSubset_AllExceptLocationInfo || + metadataSubset == kMetadataSubset_AllExceptCameraAndLocation) + { + + // Remove GPS fields. + + dng_exif blankExif; + + newEXIF.CopyGPSFrom (blankExif); + + // Remove MakerNote just in case, because we don't know + // all of what is in it. + + metadata.ClearMakerNote (); + + // Remove XMP & IPTC location fields. + + newXMP.Remove (XMP_NS_PHOTOSHOP, "City"); + newXMP.Remove (XMP_NS_PHOTOSHOP, "State"); + newXMP.Remove (XMP_NS_PHOTOSHOP, "Country"); + newXMP.Remove (XMP_NS_IPTC, "Location"); + newXMP.Remove (XMP_NS_IPTC, "CountryCode"); + newXMP.Remove (XMP_NS_IPTC_EXT, "LocationCreated"); + newXMP.Remove (XMP_NS_IPTC_EXT, "LocationShown"); + + } + + } + + // Rebuild the legacy IPTC block, if needed. + + bool isTIFF = (strcmp (dstMIMI, "image/tiff") == 0); + bool isDNG = (strcmp (dstMIMI, "image/dng" ) == 0); + + if (!isDNG) + { + + metadata.RebuildIPTC (host.Allocator (), + isTIFF); + + } + + else + { + + metadata.ClearIPTC (); + + } + + // Clear format related XMP. + + newXMP.ClearOrientation (); + + newXMP.ClearImageInfo (); + + newXMP.RemoveProperties (XMP_NS_DNG); + + // All the formats we care about already keep the IPTC digest + // elsewhere, do we don't need to write it to the XMP. + + newXMP.ClearIPTCDigest (); + + // Make sure that sidecar specific tags never get written to files. + + newXMP.Remove (XMP_NS_PHOTOSHOP, "SidecarForExtension"); + newXMP.Remove (XMP_NS_PHOTOSHOP, "EmbeddedXMPDigest"); + + } + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteTIFF (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation, + uint32 compression, + dng_negative *negative, + const dng_color_space *space, + const dng_resolution *resolution, + const dng_jpeg_preview *thumbnail, + const dng_memory_block *imageResources, + dng_metadata_subset metadataSubset) + { + + WriteTIFF (host, + stream, + image, + photometricInterpretation, + compression, + negative ? &(negative->Metadata ()) : NULL, + space, + resolution, + thumbnail, + imageResources, + metadataSubset); + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteTIFF (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation, + uint32 compression, + const dng_metadata *metadata, + const dng_color_space *space, + const dng_resolution *resolution, + const dng_jpeg_preview *thumbnail, + const dng_memory_block *imageResources, + dng_metadata_subset metadataSubset) + { + + const void *profileData = NULL; + uint32 profileSize = 0; + + const uint8 *data = NULL; + uint32 size = 0; + + if (space && space->ICCProfile (size, data)) + { + + profileData = data; + profileSize = size; + + } + + WriteTIFFWithProfile (host, + stream, + image, + photometricInterpretation, + compression, + metadata, + profileData, + profileSize, + resolution, + thumbnail, + imageResources, + metadataSubset); + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteTIFFWithProfile (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation, + uint32 compression, + dng_negative *negative, + const void *profileData, + uint32 profileSize, + const dng_resolution *resolution, + const dng_jpeg_preview *thumbnail, + const dng_memory_block *imageResources, + dng_metadata_subset metadataSubset) + { + + WriteTIFFWithProfile (host, + stream, + image, + photometricInterpretation, + compression, + negative ? &(negative->Metadata ()) : NULL, + profileData, + profileSize, + resolution, + thumbnail, + imageResources, + metadataSubset); + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteTIFFWithProfile (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation, + uint32 compression, + const dng_metadata *constMetadata, + const void *profileData, + uint32 profileSize, + const dng_resolution *resolution, + const dng_jpeg_preview *thumbnail, + const dng_memory_block *imageResources, + dng_metadata_subset metadataSubset) + { + + uint32 j; + + AutoPtr metadata; + + if (constMetadata) + { + + metadata.Reset (constMetadata->Clone (host.Allocator ())); + + CleanUpMetadata (host, + *metadata, + metadataSubset, + "image/tiff"); + + } + + dng_ifd ifd; + + ifd.fNewSubFileType = sfMainImage; + + ifd.fImageWidth = image.Bounds ().W (); + ifd.fImageLength = image.Bounds ().H (); + + ifd.fSamplesPerPixel = image.Planes (); + + ifd.fBitsPerSample [0] = TagTypeSize (image.PixelType ()) * 8; + + for (j = 1; j < ifd.fSamplesPerPixel; j++) + { + ifd.fBitsPerSample [j] = ifd.fBitsPerSample [0]; + } + + ifd.fPhotometricInterpretation = photometricInterpretation; + + ifd.fCompression = compression; + + if (ifd.fCompression == ccUncompressed) + { + + ifd.SetSingleStrip (); + + } + + else + { + + ifd.FindStripSize (128 * 1024); + + ifd.fPredictor = cpHorizontalDifference; + + } + + uint32 extraSamples = 0; + + switch (photometricInterpretation) + { + + case piBlackIsZero: + { + extraSamples = image.Planes () - 1; + break; + } + + case piRGB: + { + extraSamples = image.Planes () - 3; + break; + } + + default: + break; + + } + + ifd.fExtraSamplesCount = extraSamples; + + if (image.PixelType () == ttFloat) + { + + for (j = 0; j < ifd.fSamplesPerPixel; j++) + { + ifd.fSampleFormat [j] = sfFloatingPoint; + } + + } + + dng_tiff_directory mainIFD; + + dng_basic_tag_set basic (mainIFD, ifd); + + // Resolution. + + dng_resolution res; + + if (resolution) + { + res = *resolution; + } + + tag_urational tagXResolution (tcXResolution, res.fXResolution); + tag_urational tagYResolution (tcYResolution, res.fYResolution); + + tag_uint16 tagResolutionUnit (tcResolutionUnit, res.fResolutionUnit); + + if (resolution) + { + mainIFD.Add (&tagXResolution ); + mainIFD.Add (&tagYResolution ); + mainIFD.Add (&tagResolutionUnit); + } + + // ICC Profile. + + tag_icc_profile iccProfileTag (profileData, profileSize); + + if (iccProfileTag.Count ()) + { + mainIFD.Add (&iccProfileTag); + } + + // XMP metadata. + + tag_xmp tagXMP (metadata.Get () ? metadata->GetXMP () : NULL); + + if (tagXMP.Count ()) + { + mainIFD.Add (&tagXMP); + } + + // IPTC metadata. + + tag_iptc tagIPTC (metadata.Get () ? metadata->IPTCData () : NULL, + metadata.Get () ? metadata->IPTCLength () : 0); + + if (tagIPTC.Count ()) + { + mainIFD.Add (&tagIPTC); + } + + // Adobe data (thumbnail and IPTC digest) + + AutoPtr adobeData (BuildAdobeData (host, + metadata.Get (), + thumbnail, + imageResources)); + + tag_uint8_ptr tagAdobe (tcAdobeData, + adobeData->Buffer_uint8 (), + adobeData->LogicalSize ()); + + if (tagAdobe.Count ()) + { + mainIFD.Add (&tagAdobe); + } + + // Exif metadata. + + exif_tag_set exifSet (mainIFD, + metadata.Get () && metadata->GetExif () ? *metadata->GetExif () + : dng_exif (), + metadata.Get () ? metadata->IsMakerNoteSafe () : false, + metadata.Get () ? metadata->MakerNoteData () : NULL, + metadata.Get () ? metadata->MakerNoteLength () : 0, + false); + + // Find offset to main image data. + + uint32 offsetMainIFD = 8; + + uint32 offsetExifData = offsetMainIFD + mainIFD.Size (); + + exifSet.Locate (offsetExifData); + + uint32 offsetMainData = offsetExifData + exifSet.Size (); + + stream.SetWritePosition (offsetMainData); + + // Write the main image data. + + WriteImage (host, + ifd, + basic, + stream, + image); + + // Trim the file to this length. + + stream.SetLength (stream.Position ()); + + // TIFF has a 4G size limit. + + if (stream.Length () > 0x0FFFFFFFFL) + { + ThrowImageTooBigTIFF (); + } + + // Write TIFF Header. + + stream.SetWritePosition (0); + + stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII); + + stream.Put_uint16 (42); + + stream.Put_uint32 (offsetMainIFD); + + // Write the IFDs. + + mainIFD.Put (stream); + + exifSet.Put (stream); + + stream.Flush (); + + } + +/*****************************************************************************/ + +void dng_image_writer::WriteDNG (dng_host &host, + dng_stream &stream, + dng_negative &negative, + const dng_preview_list *previewList, + uint32 maxBackwardVersion, + bool uncompressed) + { +TIMESTAMP("[BEG]", 2) + + WriteDNG (host, + stream, + negative, + negative.Metadata (), + previewList, + maxBackwardVersion, + uncompressed); + +TIMESTAMP("[END]", 2) + } + +/*****************************************************************************/ + +void dng_image_writer::WriteDNG (dng_host &host, + dng_stream &stream, + const dng_negative &negative, + const dng_metadata &constMetadata, + const dng_preview_list *previewList, + uint32 maxBackwardVersion, + bool uncompressed) + { + + uint32 j; + + // Clean up metadata per MWG recommendations. + + + AutoPtr metadata (constMetadata.Clone (host.Allocator ())); + +#if METADATA_CLEANUP + CleanUpMetadata (host, + *metadata, + kMetadataSubset_All, + "image/dng"); +#endif + +#if GPR_WRITING + // Figure out the compression to use. Can be lossless JPEG or VC5 + uint32 compression = uncompressed ? ccUncompressed : GetDefaultCompression(); + +#else + // Figure out the compression to use. Most of the time this is lossless + // JPEG. + uint32 compression = uncompressed ? ccUncompressed : ccJPEG; +#endif + + + + // Was the the original file lossy JPEG compressed? + + const dng_jpeg_image *rawJPEGImage = negative.RawJPEGImage (); + + // If so, can we save it using the requested compression and DNG version? + + if (uncompressed || maxBackwardVersion < dngVersion_1_4_0_0) + { + + if (rawJPEGImage || negative.RawJPEGImageDigest ().IsValid ()) + { + + rawJPEGImage = NULL; + + negative.ClearRawJPEGImageDigest (); + + negative.ClearRawImageDigest (); + + } + + } + + else if (rawJPEGImage) + { + + compression = ccLossyJPEG; + + } + + // Are we saving the original size tags? + + bool saveOriginalDefaultFinalSize = false; + bool saveOriginalBestQualityFinalSize = false; + bool saveOriginalDefaultCropSize = false; + + { + + // See if we are saving a proxy image. + + dng_point defaultFinalSize (negative.DefaultFinalHeight (), + negative.DefaultFinalWidth ()); + + saveOriginalDefaultFinalSize = (negative.OriginalDefaultFinalSize () != + defaultFinalSize); + + if (saveOriginalDefaultFinalSize) + { + + // If the save OriginalDefaultFinalSize tag, this changes the defaults + // for the OriginalBestQualityFinalSize and OriginalDefaultCropSize tags. + + saveOriginalBestQualityFinalSize = (negative.OriginalBestQualityFinalSize () != + defaultFinalSize); + + saveOriginalDefaultCropSize = (negative.OriginalDefaultCropSizeV () != + dng_urational (defaultFinalSize.v, 1)) || + (negative.OriginalDefaultCropSizeH () != + dng_urational (defaultFinalSize.h, 1)); + + } + + else + { + + // Else these two tags default to the normal non-proxy size image values. + + dng_point bestQualityFinalSize (negative.BestQualityFinalHeight (), + negative.BestQualityFinalWidth ()); + + saveOriginalBestQualityFinalSize = (negative.OriginalBestQualityFinalSize () != + bestQualityFinalSize); + + saveOriginalDefaultCropSize = (negative.OriginalDefaultCropSizeV () != + negative.DefaultCropSizeV ()) || + (negative.OriginalDefaultCropSizeH () != + negative.DefaultCropSizeH ()); + + } + + } + + // Is this a floating point image that we are saving? + + bool isFloatingPoint = (negative.RawImage ().PixelType () == ttFloat); + + // Does this image have a transparency mask? + + bool hasTransparencyMask = (negative.RawTransparencyMask () != NULL); + + // Should we save a compressed 32-bit integer file? + + bool isCompressed32BitInteger = (negative.RawImage ().PixelType () == ttLong) && + (maxBackwardVersion >= dngVersion_1_4_0_0) && + (!uncompressed); + + // Figure out what main version to use. + + uint32 dngVersion = dngVersion_Current; + + // Don't write version 1.4 files unless we actually use some feature of the 1.4 spec. + + if (dngVersion == dngVersion_1_4_0_0) + { + + if (!rawJPEGImage && + !isFloatingPoint && + !hasTransparencyMask && + !isCompressed32BitInteger && + !saveOriginalDefaultFinalSize && + !saveOriginalBestQualityFinalSize && + !saveOriginalDefaultCropSize ) + { + + dngVersion = dngVersion_1_3_0_0; + + } + + } + + // Figure out what backward version to use. + + uint32 dngBackwardVersion = dngVersion_1_1_0_0; + + #if defined(qTestRowInterleave) || defined(qTestSubTileBlockRows) || defined(qTestSubTileBlockCols) + dngBackwardVersion = Max_uint32 (dngBackwardVersion, dngVersion_1_2_0_0); + #endif + + dngBackwardVersion = Max_uint32 (dngBackwardVersion, + negative.OpcodeList1 ().MinVersion (false)); + + dngBackwardVersion = Max_uint32 (dngBackwardVersion, + negative.OpcodeList2 ().MinVersion (false)); + + dngBackwardVersion = Max_uint32 (dngBackwardVersion, + negative.OpcodeList3 ().MinVersion (false)); + + if (negative.GetMosaicInfo () && + negative.GetMosaicInfo ()->fCFALayout >= 6) + { + dngBackwardVersion = Max_uint32 (dngBackwardVersion, dngVersion_1_3_0_0); + } + + if (rawJPEGImage || isFloatingPoint || hasTransparencyMask || isCompressed32BitInteger) + { + dngBackwardVersion = Max_uint32 (dngBackwardVersion, dngVersion_1_4_0_0); + } + + if (dngBackwardVersion > dngVersion) + { + ThrowProgramError (); + } + + // Find best thumbnail from preview list, if any. + + const dng_preview *thumbnail = NULL; + + if (previewList) + { + + uint32 thumbArea = 0; + + for (j = 0; j < previewList->Count (); j++) + { + + const dng_image_preview *imagePreview = dynamic_cast(&previewList->Preview (j)); + + if (imagePreview) + { + + uint32 thisArea = imagePreview->fImage->Bounds ().W () * + imagePreview->fImage->Bounds ().H (); + + if (!thumbnail || thisArea < thumbArea) + { + + thumbnail = &previewList->Preview (j); + + thumbArea = thisArea; + + } + + } + + const dng_jpeg_preview *jpegPreview = dynamic_cast(&previewList->Preview (j)); + + if (jpegPreview) + { + + uint32 thisArea = jpegPreview->fPreviewSize.h * + jpegPreview->fPreviewSize.v; + + if (!thumbnail || thisArea < thumbArea) + { + + thumbnail = &previewList->Preview (j); + + thumbArea = thisArea; + + } + + } + + } + + } + + // Create the main IFD + dng_tiff_directory mainIFD; + + // Create the IFD for the raw data. If there is no thumnail, this is + // just a reference the main IFD. Otherwise allocate a new one. + + AutoPtr rawIFD_IfNotMain; + + if (thumbnail) + { + rawIFD_IfNotMain.Reset (new dng_tiff_directory); + } + + dng_tiff_directory &rawIFD (thumbnail ? *rawIFD_IfNotMain : mainIFD); + + // Include DNG version tags. + + uint8 dngVersionData [4]; + + dngVersionData [0] = (uint8) (dngVersion >> 24); + dngVersionData [1] = (uint8) (dngVersion >> 16); + dngVersionData [2] = (uint8) (dngVersion >> 8); + dngVersionData [3] = (uint8) (dngVersion ); + + tag_uint8_ptr tagDNGVersion (tcDNGVersion, dngVersionData, 4); + + mainIFD.Add (&tagDNGVersion); + + uint8 dngBackwardVersionData [4]; + + dngBackwardVersionData [0] = (uint8) (dngBackwardVersion >> 24); + dngBackwardVersionData [1] = (uint8) (dngBackwardVersion >> 16); + dngBackwardVersionData [2] = (uint8) (dngBackwardVersion >> 8); + dngBackwardVersionData [3] = (uint8) (dngBackwardVersion ); + + tag_uint8_ptr tagDNGBackwardVersion (tcDNGBackwardVersion, dngBackwardVersionData, 4); + + mainIFD.Add (&tagDNGBackwardVersion); + + // The main IFD contains the thumbnail, if there is a thumbnail. + + AutoPtr thmBasic; + + if (thumbnail) + { + thmBasic.Reset (thumbnail->AddTagSet (mainIFD)); + } + + // Get the raw image we are writing. + + const dng_image &rawImage (negative.RawImage ()); + + // For floating point, we only support ZIP compression. + + if (isFloatingPoint && !uncompressed) + { + + compression = ccDeflate; + + } + + // For 32-bit integer images, we only support ZIP and uncompressed. + + if (rawImage.PixelType () == ttLong) + { + + if (isCompressed32BitInteger) + { + compression = ccDeflate; + } + + else + { + compression = ccUncompressed; + } + + } + + // Get a copy of the mosaic info. + + dng_mosaic_info mosaicInfo; + + if (negative.GetMosaicInfo ()) + { + mosaicInfo = *(negative.GetMosaicInfo ()); + } + + // Create a dng_ifd record for the raw image. + + dng_ifd info; + + info.fImageWidth = rawImage.Width (); + info.fImageLength = rawImage.Height (); + + info.fSamplesPerPixel = rawImage.Planes (); + + info.fPhotometricInterpretation = mosaicInfo.IsColorFilterArray () ? piCFA + : piLinearRaw; + + info.fCompression = compression; + + if (isFloatingPoint && compression == ccDeflate) + { + + info.fPredictor = cpFloatingPoint; + + if (mosaicInfo.IsColorFilterArray ()) + { + + if (mosaicInfo.fCFAPatternSize.h == 2) + { + info.fPredictor = cpFloatingPointX2; + } + + else if (mosaicInfo.fCFAPatternSize.h == 4) + { + info.fPredictor = cpFloatingPointX4; + } + + } + + } + + if (isCompressed32BitInteger) + { + + info.fPredictor = cpHorizontalDifference; + + if (mosaicInfo.IsColorFilterArray ()) + { + + if (mosaicInfo.fCFAPatternSize.h == 2) + { + info.fPredictor = cpHorizontalDifferenceX2; + } + + else if (mosaicInfo.fCFAPatternSize.h == 4) + { + info.fPredictor = cpHorizontalDifferenceX4; + } + + } + + } + + uint32 rawPixelType = rawImage.PixelType (); + + if (rawPixelType == ttShort) + { + + // See if we are using a linearization table with <= 256 entries, in which + // case the useful data will all fit within 8-bits. + + const dng_linearization_info *rangeInfo = negative.GetLinearizationInfo (); + + if (rangeInfo) + { + + if (rangeInfo->fLinearizationTable.Get ()) + { + + uint32 entries = rangeInfo->fLinearizationTable->LogicalSize () >> 1; + + if (entries <= 256) + { + + rawPixelType = ttByte; + + } + + } + + } + + } + + switch (rawPixelType) + { + + case ttByte: + { + info.fBitsPerSample [0] = 8; + break; + } + + case ttShort: + { + info.fBitsPerSample [0] = 16; + break; + } + + case ttLong: + { + info.fBitsPerSample [0] = 32; + break; + } + + case ttFloat: + { + + if (negative.RawFloatBitDepth () == 16) + { + info.fBitsPerSample [0] = 16; + } + + else if (negative.RawFloatBitDepth () == 24) + { + info.fBitsPerSample [0] = 24; + } + + else + { + info.fBitsPerSample [0] = 32; + } + + for (j = 0; j < info.fSamplesPerPixel; j++) + { + info.fSampleFormat [j] = sfFloatingPoint; + } + + break; + + } + + default: + { + ThrowProgramError (); + } + + } + + // For lossless JPEG compression, we often lie about the + // actual channel count to get the predictors to work across + // same color mosaic pixels. + + uint32 fakeChannels = 1; + + if (info.fCompression == ccJPEG) + { + + if (mosaicInfo.IsColorFilterArray ()) + { + + if (mosaicInfo.fCFAPatternSize.h == 4) + { + fakeChannels = 4; + } + + else if (mosaicInfo.fCFAPatternSize.h == 2) + { + fakeChannels = 2; + } + + // However, lossless JEPG is limited to four channels, + // so compromise might be required. + + while (fakeChannels * info.fSamplesPerPixel > 4 && + fakeChannels > 1) + { + + fakeChannels >>= 1; + + } + + } + + } + + // Figure out tile sizes. + + if (rawJPEGImage) + { + + DNG_ASSERT (rawPixelType == ttByte, + "Unexpected jpeg pixel type"); + + DNG_ASSERT (info.fImageWidth == (uint32) rawJPEGImage->fImageSize.h && + info.fImageLength == (uint32) rawJPEGImage->fImageSize.v, + "Unexpected jpeg image size"); + + info.fTileWidth = rawJPEGImage->fTileSize.h; + info.fTileLength = rawJPEGImage->fTileSize.v; + + info.fUsesStrips = rawJPEGImage->fUsesStrips; + + info.fUsesTiles = !info.fUsesStrips; + + } + + else if (info.fCompression == ccJPEG) + { + + info.FindTileSize (128 * 1024); + + } + + else if (info.fCompression == ccDeflate) + { + + info.FindTileSize (512 * 1024); + + } + + else if (info.fCompression == ccLossyJPEG) + { + + ThrowProgramError ("No JPEG compressed image"); + + } +#if GPR_WRITING + else if (info.fCompression == ccVc5) + { + info.fTileWidth = info.fImageWidth; + info.fTileLength = info.fImageLength; + + info.fUsesStrips = false; + + info.fUsesTiles = true; + } +#endif + // Don't use tiles for uncompressed images. + + else + { + + info.SetSingleStrip (); + + } + + #ifdef qTestRowInterleave + + info.fRowInterleaveFactor = qTestRowInterleave; + + #endif + + #if defined(qTestSubTileBlockRows) && defined(qTestSubTileBlockCols) + + info.fSubTileBlockRows = qTestSubTileBlockRows; + info.fSubTileBlockCols = qTestSubTileBlockCols; + + if (fakeChannels == 2) + fakeChannels = 4; + + #endif + + // Basic information. + + dng_basic_tag_set rawBasic (rawIFD, info); + + // JPEG tables, if any. + + tag_data_ptr tagJPEGTables (tcJPEGTables, + ttUndefined, + 0, + NULL); + + if (rawJPEGImage && rawJPEGImage->fJPEGTables.Get ()) + { + + tagJPEGTables.SetData (rawJPEGImage->fJPEGTables->Buffer ()); + + tagJPEGTables.SetCount (rawJPEGImage->fJPEGTables->LogicalSize ()); + + rawIFD.Add (&tagJPEGTables); + + } + + // DefaultScale tag. + dng_urational defaultScaleData [2]; + + defaultScaleData [0] = negative.DefaultScaleH (); + defaultScaleData [1] = negative.DefaultScaleV (); + + tag_urational_ptr tagDefaultScale (tcDefaultScale, + defaultScaleData, + 2); + + rawIFD.Add (&tagDefaultScale); + // Best quality scale tag. + + tag_urational tagBestQualityScale (tcBestQualityScale, + negative.BestQualityScale ()); + + rawIFD.Add (&tagBestQualityScale); + + // DefaultCropOrigin tag. + + dng_urational defaultCropOriginData [2]; + + defaultCropOriginData [0] = negative.DefaultCropOriginH (); + defaultCropOriginData [1] = negative.DefaultCropOriginV (); + + tag_urational_ptr tagDefaultCropOrigin (tcDefaultCropOrigin, + defaultCropOriginData, + 2); + + rawIFD.Add (&tagDefaultCropOrigin); + + // DefaultCropSize tag. + + dng_urational defaultCropSizeData [2]; + + defaultCropSizeData [0] = negative.DefaultCropSizeH (); + defaultCropSizeData [1] = negative.DefaultCropSizeV (); + + tag_urational_ptr tagDefaultCropSize (tcDefaultCropSize, + defaultCropSizeData, + 2); + + rawIFD.Add (&tagDefaultCropSize); + + // DefaultUserCrop tag. + + dng_urational defaultUserCropData [4]; + defaultUserCropData [0] = negative.DefaultUserCropT (); + defaultUserCropData [1] = negative.DefaultUserCropL (); + defaultUserCropData [2] = negative.DefaultUserCropB (); + defaultUserCropData [3] = negative.DefaultUserCropR (); + + tag_urational_ptr tagDefaultUserCrop (tcDefaultUserCrop, + defaultUserCropData, + 4); + + rawIFD.Add (&tagDefaultUserCrop); + + // Range mapping tag set. + + range_tag_set rangeSet (rawIFD, negative); + + // Mosaic pattern information. + + mosaic_tag_set mosaicSet (rawIFD, mosaicInfo); + + // Chroma blur radius. + + tag_urational tagChromaBlurRadius (tcChromaBlurRadius, + negative.ChromaBlurRadius ()); + + if (negative.ChromaBlurRadius ().IsValid ()) + { + + rawIFD.Add (&tagChromaBlurRadius); + + } + + // Anti-alias filter strength. + + tag_urational tagAntiAliasStrength (tcAntiAliasStrength, + negative.AntiAliasStrength ()); + + if (negative.AntiAliasStrength ().IsValid ()) + { + + rawIFD.Add (&tagAntiAliasStrength); + + } + // Profile and other color related tags. + + AutoPtr profileSet; + + AutoPtr colorSet; + + std::vector extraProfileIndex; + + if (!negative.IsMonochrome ()) + { + const dng_camera_profile &mainProfile (*negative.ComputeCameraProfileToEmbed (constMetadata)); + profileSet.Reset (new profile_tag_set (mainIFD, + mainProfile)); + colorSet.Reset (new color_tag_set (mainIFD, + negative)); + // Build list of profile indices to include in extra profiles tag. + + uint32 profileCount = negative.ProfileCount (); + for (uint32 index = 0; index < profileCount; index++) + { + + const dng_camera_profile &profile (negative.ProfileByIndex (index)); + + if (&profile != &mainProfile) + { + + if (profile.WasReadFromDNG ()) + { + + extraProfileIndex.push_back (index); + + } + + } + + } + + } + // Extra camera profiles tag. + + uint32 extraProfileCount = (uint32) extraProfileIndex.size (); + + dng_memory_data extraProfileOffsets (extraProfileCount * (uint32) sizeof (uint32)); + + tag_uint32_ptr extraProfileTag (tcExtraCameraProfiles, + extraProfileOffsets.Buffer_uint32 (), + extraProfileCount); + + if (extraProfileCount) + { + + mainIFD.Add (&extraProfileTag); + + } + // Other tags. + + tag_uint16 tagOrientation (tcOrientation, + (uint16) negative.ComputeOrientation (constMetadata).GetTIFF ()); + + mainIFD.Add (&tagOrientation); + + tag_srational tagBaselineExposure (tcBaselineExposure, + negative.BaselineExposureR ()); + + mainIFD.Add (&tagBaselineExposure); + + tag_urational tagBaselineNoise (tcBaselineNoise, + negative.BaselineNoiseR ()); + + mainIFD.Add (&tagBaselineNoise); + + tag_urational tagNoiseReductionApplied (tcNoiseReductionApplied, + negative.NoiseReductionApplied ()); + if (negative.NoiseReductionApplied ().IsValid ()) + { + + mainIFD.Add (&tagNoiseReductionApplied); + + } + + tag_dng_noise_profile tagNoiseProfile (negative.NoiseProfile ()); + + if (negative.NoiseProfile ().IsValidForNegative (negative)) + { + + mainIFD.Add (&tagNoiseProfile); + + } + tag_urational tagBaselineSharpness (tcBaselineSharpness, + negative.BaselineSharpnessR ()); + + mainIFD.Add (&tagBaselineSharpness); + + tag_string tagUniqueName (tcUniqueCameraModel, + negative.ModelName (), + true); + + mainIFD.Add (&tagUniqueName); + + tag_string tagLocalName (tcLocalizedCameraModel, + negative.LocalName (), + false); + if (negative.LocalName ().NotEmpty ()) + { + + mainIFD.Add (&tagLocalName); + + } + + tag_urational tagShadowScale (tcShadowScale, + negative.ShadowScaleR ()); + + mainIFD.Add (&tagShadowScale); + tag_uint16 tagColorimetricReference (tcColorimetricReference, + (uint16) negative.ColorimetricReference ()); + + if (negative.ColorimetricReference () != crSceneReferred) + { + + mainIFD.Add (&tagColorimetricReference); + + } + bool useNewDigest = (maxBackwardVersion >= dngVersion_1_4_0_0); + if (compression == ccLossyJPEG) + { + + negative.FindRawJPEGImageDigest (host); + + } + else if( fComputeMd5Sum ) + { + + if (useNewDigest) + { + negative.FindNewRawImageDigest (host); + } + else + { + negative.FindRawImageDigest (host); + } + + } + tag_uint8_ptr tagRawImageDigest (useNewDigest ? tcNewRawImageDigest : tcRawImageDigest, + compression == ccLossyJPEG ? + negative.RawJPEGImageDigest ().data : + (useNewDigest ? negative.NewRawImageDigest ().data + : negative.RawImageDigest ().data), + 16); + mainIFD.Add (&tagRawImageDigest); + + if( fComputeMd5Sum ) + { + negative.FindRawDataUniqueID (host); + } + + tag_uint8_ptr tagRawDataUniqueID (tcRawDataUniqueID, + negative.RawDataUniqueID ().data, + 16); + if (negative.RawDataUniqueID ().IsValid ()) + { + + mainIFD.Add (&tagRawDataUniqueID); + + } + tag_string tagOriginalRawFileName (tcOriginalRawFileName, + negative.OriginalRawFileName (), + false); + if (negative.HasOriginalRawFileName ()) + { + + mainIFD.Add (&tagOriginalRawFileName); + + } + negative.FindOriginalRawFileDigest (); + tag_data_ptr tagOriginalRawFileData (tcOriginalRawFileData, + ttUndefined, + negative.OriginalRawFileDataLength (), + negative.OriginalRawFileData ()); + + tag_uint8_ptr tagOriginalRawFileDigest (tcOriginalRawFileDigest, + negative.OriginalRawFileDigest ().data, + 16); + if (negative.OriginalRawFileData ()) + { + + mainIFD.Add (&tagOriginalRawFileData); + + mainIFD.Add (&tagOriginalRawFileDigest); + + } + + // XMP metadata. + tag_xmp tagXMP (metadata->GetXMP ()); + + if (tagXMP.Count ()) + { + + mainIFD.Add (&tagXMP); + + } + // Exif tags. + + exif_tag_set exifSet (mainIFD, + *metadata->GetExif (), + metadata->IsMakerNoteSafe (), + metadata->MakerNoteData (), + metadata->MakerNoteLength (), + true); + // Private data. + + tag_uint8_ptr tagPrivateData (tcDNGPrivateData, + negative.PrivateData (), + negative.PrivateLength ()); + + if (negative.PrivateLength ()) + { + + mainIFD.Add (&tagPrivateData); + + } + + // Proxy size tags. + + uint32 originalDefaultFinalSizeData [2]; + + originalDefaultFinalSizeData [0] = negative.OriginalDefaultFinalSize ().h; + originalDefaultFinalSizeData [1] = negative.OriginalDefaultFinalSize ().v; + + tag_uint32_ptr tagOriginalDefaultFinalSize (tcOriginalDefaultFinalSize, + originalDefaultFinalSizeData, + 2); + + if (saveOriginalDefaultFinalSize) + { + + mainIFD.Add (&tagOriginalDefaultFinalSize); + + } + + uint32 originalBestQualityFinalSizeData [2]; + + originalBestQualityFinalSizeData [0] = negative.OriginalBestQualityFinalSize ().h; + originalBestQualityFinalSizeData [1] = negative.OriginalBestQualityFinalSize ().v; + + tag_uint32_ptr tagOriginalBestQualityFinalSize (tcOriginalBestQualityFinalSize, + originalBestQualityFinalSizeData, + 2); + if (saveOriginalBestQualityFinalSize) + { + + mainIFD.Add (&tagOriginalBestQualityFinalSize); + + } + dng_urational originalDefaultCropSizeData [2]; + + originalDefaultCropSizeData [0] = negative.OriginalDefaultCropSizeH (); + originalDefaultCropSizeData [1] = negative.OriginalDefaultCropSizeV (); + + tag_urational_ptr tagOriginalDefaultCropSize (tcOriginalDefaultCropSize, + originalDefaultCropSizeData, + 2); + if (saveOriginalDefaultCropSize) + { + + mainIFD.Add (&tagOriginalDefaultCropSize); + + } + + // Opcode list 1. + + AutoPtr opcodeList1Data (negative.OpcodeList1 ().Spool (host)); + + tag_data_ptr tagOpcodeList1 (tcOpcodeList1, + ttUndefined, + opcodeList1Data.Get () ? opcodeList1Data->LogicalSize () : 0, + opcodeList1Data.Get () ? opcodeList1Data->Buffer () : NULL); + + if (opcodeList1Data.Get ()) + { + + rawIFD.Add (&tagOpcodeList1); + + } + + // Opcode list 2. + + AutoPtr opcodeList2Data (negative.OpcodeList2 ().Spool (host)); + + tag_data_ptr tagOpcodeList2 (tcOpcodeList2, + ttUndefined, + opcodeList2Data.Get () ? opcodeList2Data->LogicalSize () : 0, + opcodeList2Data.Get () ? opcodeList2Data->Buffer () : NULL); + + if (opcodeList2Data.Get ()) + { + + rawIFD.Add (&tagOpcodeList2); + + } + // Opcode list 3. + + AutoPtr opcodeList3Data (negative.OpcodeList3 ().Spool (host)); + + tag_data_ptr tagOpcodeList3 (tcOpcodeList3, + ttUndefined, + opcodeList3Data.Get () ? opcodeList3Data->LogicalSize () : 0, + opcodeList3Data.Get () ? opcodeList3Data->Buffer () : NULL); + + if (opcodeList3Data.Get ()) + { + + rawIFD.Add (&tagOpcodeList3); + + } + + // Transparency mask, if any. + + AutoPtr maskInfo; + + AutoPtr maskIFD; + + AutoPtr maskBasic; + + if (hasTransparencyMask) + { + + // Create mask IFD. + + maskInfo.Reset (new dng_ifd); + + maskInfo->fNewSubFileType = sfTransparencyMask; + + maskInfo->fImageWidth = negative.RawTransparencyMask ()->Bounds ().W (); + maskInfo->fImageLength = negative.RawTransparencyMask ()->Bounds ().H (); + + maskInfo->fSamplesPerPixel = 1; + + maskInfo->fBitsPerSample [0] = negative.RawTransparencyMaskBitDepth (); + + maskInfo->fPhotometricInterpretation = piTransparencyMask; + + maskInfo->fCompression = uncompressed ? ccUncompressed : ccDeflate; + maskInfo->fPredictor = uncompressed ? cpNullPredictor : cpHorizontalDifference; + + if (negative.RawTransparencyMask ()->PixelType () == ttFloat) + { + + maskInfo->fSampleFormat [0] = sfFloatingPoint; + + if (maskInfo->fCompression == ccDeflate) + { + maskInfo->fPredictor = cpFloatingPoint; + } + + } + + if (maskInfo->fCompression == ccDeflate) + { + maskInfo->FindTileSize (512 * 1024); + } + else + { + maskInfo->SetSingleStrip (); + } + + // Create mask tiff directory. + + maskIFD.Reset (new dng_tiff_directory); + + // Add mask basic tag set. + + maskBasic.Reset (new dng_basic_tag_set (*maskIFD, *maskInfo)); + + } + + // Add other subfiles. + + uint32 subFileCount = thumbnail ? 1 : 0; + + if (hasTransparencyMask) + { + subFileCount++; + } + + // Add previews. + + uint32 previewCount = previewList ? previewList->Count () : 0; + + AutoPtr previewIFD [kMaxDNGPreviews]; + + AutoPtr previewBasic [kMaxDNGPreviews]; + + for (j = 0; j < previewCount; j++) + { + + if (thumbnail != &previewList->Preview (j)) + { + + previewIFD [j] . Reset (new dng_tiff_directory); + + previewBasic [j] . Reset (previewList->Preview (j).AddTagSet (*previewIFD [j])); + + subFileCount++; + + } + + } + + // And a link to the raw and JPEG image IFDs. + + uint32 subFileData [kMaxDNGPreviews + 2]; + + tag_uint32_ptr tagSubFile (tcSubIFDs, + subFileData, + subFileCount); + + if (subFileCount) + { + + mainIFD.Add (&tagSubFile); + + } + + // Skip past the header and IFDs for now. + + uint32 currentOffset = 8; + + currentOffset += mainIFD.Size (); + + uint32 subFileIndex = 0; + + if (thumbnail) + { + + subFileData [subFileIndex++] = currentOffset; + + currentOffset += rawIFD.Size (); + + } + + if (hasTransparencyMask) + { + + subFileData [subFileIndex++] = currentOffset; + + currentOffset += maskIFD->Size (); + + } + + for (j = 0; j < previewCount; j++) + { + + if (thumbnail != &previewList->Preview (j)) + { + + subFileData [subFileIndex++] = currentOffset; + + currentOffset += previewIFD [j]->Size (); + + } + + } + + exifSet.Locate (currentOffset); + + currentOffset += exifSet.Size (); + + stream.SetWritePosition (currentOffset); + + // Write the extra profiles. + + if (extraProfileCount) + { + + for (j = 0; j < extraProfileCount; j++) + { + + extraProfileOffsets.Buffer_uint32 () [j] = (uint32) stream.Position (); + + uint32 index = extraProfileIndex [j]; + + const dng_camera_profile &profile (negative.ProfileByIndex (index)); + + tiff_dng_extended_color_profile extraWriter (profile); + + extraWriter.Put (stream, false); + + } + + } + + // Write the thumbnail data. + + if (thumbnail) + { + + thumbnail->WriteData (host, + *this, + *thmBasic, + stream); + + } + + // Write the preview data. + + for (j = 0; j < previewCount; j++) + { + + if (thumbnail != &previewList->Preview (j)) + { + + previewList->Preview (j).WriteData (host, + *this, + *previewBasic [j], + stream); + + } + + } + + // Write the raw data. + + if (rawJPEGImage) + { + uint32 tileCount = info.TilesAcross () * + info.TilesDown (); + + for (uint32 tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + // Remember this offset. + + uint32 tileOffset = (uint32) stream.Position (); + + rawBasic.SetTileOffset (tileIndex, tileOffset); + + // Write JPEG data. + + stream.Put (rawJPEGImage->fJPEGData [tileIndex]->Buffer (), + rawJPEGImage->fJPEGData [tileIndex]->LogicalSize ()); + + // Update tile count. + + uint32 tileByteCount = (uint32) stream.Position () - tileOffset; + + rawBasic.SetTileByteCount (tileIndex, tileByteCount); + + // Keep the tiles on even byte offsets. + + if (tileByteCount & 1) + { + stream.Put_uint8 (0); + } + + } + } + + else + { + #if qDNGValidate + dng_timer timer ("Write raw image time"); + #endif + + WriteImage (host, + info, + rawBasic, + stream, + rawImage, + fakeChannels); + } + + // Write transparency mask image. + + if (hasTransparencyMask) + { + #if qDNGValidate + dng_timer timer ("Write transparency mask time"); + #endif + + WriteImage (host, + *maskInfo, + *maskBasic, + stream, + *negative.RawTransparencyMask ()); + } + + // Trim the file to this length. + + stream.SetLength (stream.Position ()); + + // DNG has a 4G size limit. + + if (stream.Length () > 0x0FFFFFFFFL) + { + ThrowImageTooBigDNG (); + } + + // Write TIFF Header. + + stream.SetWritePosition (0); + + stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII); + + stream.Put_uint16 (42); + + stream.Put_uint32 (8); + + // Write the IFDs. + + mainIFD.Put (stream); + + if (thumbnail) + { + + rawIFD.Put (stream); + + } + + if (hasTransparencyMask) + { + + maskIFD->Put (stream); + + } + + for (j = 0; j < previewCount; j++) + { + + if (thumbnail != &previewList->Preview (j)) + { + + previewIFD [j]->Put (stream); + + } + + } + exifSet.Put (stream); + + stream.Flush (); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_image_writer.h b/source/lib/dng_sdk/dng_image_writer.h new file mode 100644 index 0000000..23b34d1 --- /dev/null +++ b/source/lib/dng_sdk/dng_image_writer.h @@ -0,0 +1,1253 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_image_writer.h#3 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * Support for writing DNG images to files. + */ + +/*****************************************************************************/ + +#ifndef __dng_image_writer__ +#define __dng_image_writer__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_fingerprint.h" +#include "dng_memory.h" +#include "dng_point.h" +#include "dng_rational.h" +#include "dng_sdk_limits.h" +#include "dng_string.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief Image resolution. + +class dng_resolution + { + + public: + + dng_urational fXResolution; + dng_urational fYResolution; + + uint16 fResolutionUnit; + + public: + + dng_resolution (); + + }; + +/*****************************************************************************/ + +class tiff_tag + { + + protected: + + uint16 fCode; + + uint16 fType; + + uint32 fCount; + + protected: + + tiff_tag (uint16 code, + uint16 type, + uint32 count) + + : fCode (code) + , fType (type) + , fCount (count) + + { + } + + public: + + virtual ~tiff_tag () + { + } + + uint16 Code () const + { + return fCode; + } + + uint16 Type () const + { + return fType; + } + + uint32 Count () const + { + return fCount; + } + + void SetCount (uint32 count) + { + fCount = count; + } + + uint32 Size () const + { + return TagTypeSize (Type ()) * Count (); + } + + virtual void Put (dng_stream &stream) const = 0; + + private: + + // Hidden copy constructor and assignment operator. + + tiff_tag (const tiff_tag &tag); + + tiff_tag & operator= (const tiff_tag &tag); + + }; + +/******************************************************************************/ + +class tag_data_ptr: public tiff_tag + { + + protected: + + const void *fData; + + public: + + tag_data_ptr (uint16 code, + uint16 type, + uint32 count, + const void *data) + + : tiff_tag (code, type, count) + + , fData (data) + + { + } + + void SetData (const void *data) + { + fData = data; + } + + virtual void Put (dng_stream &stream) const; + + private: + + // Hidden copy constructor and assignment operator. + + tag_data_ptr (const tag_data_ptr &tag); + + tag_data_ptr & operator= (const tag_data_ptr &tag); + + }; + +/******************************************************************************/ + +class tag_string: public tiff_tag + { + + protected: + + dng_string fString; + + public: + + tag_string (uint16 code, + const dng_string &s, + bool forceASCII = true); + + virtual void Put (dng_stream &stream) const; + + }; + +/******************************************************************************/ + +class tag_encoded_text: public tiff_tag + { + + private: + + dng_string fText; + + dng_memory_data fUTF16; + + public: + + tag_encoded_text (uint16 code, + const dng_string &text); + + virtual void Put (dng_stream &stream) const; + + }; + +/******************************************************************************/ + +class tag_uint8: public tag_data_ptr + { + + private: + + uint8 fValue; + + public: + + tag_uint8 (uint16 code, + uint8 value = 0) + + : tag_data_ptr (code, ttByte, 1, &fValue) + + , fValue (value) + + { + } + + void Set (uint8 value) + { + fValue = value; + } + + }; + +/******************************************************************************/ + +class tag_uint8_ptr: public tag_data_ptr + { + + public: + + tag_uint8_ptr (uint16 code, + const uint8 *data, + uint32 count = 1) + + : tag_data_ptr (code, ttByte, count, data) + + { + } + + }; + +/******************************************************************************/ + +class tag_uint16: public tag_data_ptr + { + + private: + + uint16 fValue; + + public: + + tag_uint16 (uint16 code, + uint16 value = 0) + + : tag_data_ptr (code, ttShort, 1, &fValue) + + , fValue (value) + + { + } + + void Set (uint16 value) + { + fValue = value; + } + + }; + +/******************************************************************************/ + +class tag_int16_ptr: public tag_data_ptr + { + + public: + + tag_int16_ptr (uint16 code, + const int16 *data, + uint32 count = 1) + + : tag_data_ptr (code, ttSShort, count, data) + + { + } + + }; + +/******************************************************************************/ + +class tag_uint16_ptr: public tag_data_ptr + { + + public: + + tag_uint16_ptr (uint16 code, + const uint16 *data, + uint32 count = 1) + + : tag_data_ptr (code, ttShort, count, data) + + { + } + + }; + +/******************************************************************************/ + +class tag_uint32: public tag_data_ptr + { + + private: + + uint32 fValue; + + public: + + tag_uint32 (uint16 code, + uint32 value = 0) + + : tag_data_ptr (code, ttLong, 1, &fValue) + + , fValue (value) + + { + } + + void Set (uint32 value) + { + fValue = value; + } + + }; + +/******************************************************************************/ + +class tag_uint32_ptr: public tag_data_ptr + { + + public: + + tag_uint32_ptr (uint16 code, + const uint32 *data, + uint32 count = 1) + + : tag_data_ptr (code, ttLong, count, data) + + { + } + + }; + +/******************************************************************************/ + +class tag_urational: public tag_data_ptr + { + + private: + + const dng_urational fValue; + + public: + + tag_urational (uint16 code, + const dng_urational &value) + + : tag_data_ptr (code, ttRational, 1, &fValue) + + , fValue (value) + + { + } + + }; + +/******************************************************************************/ + +class tag_urational_ptr: public tag_data_ptr + { + + public: + + tag_urational_ptr (uint16 code, + const dng_urational *data = NULL, + uint32 count = 1) + + : tag_data_ptr (code, ttRational, count, data) + + { + } + + }; + +/******************************************************************************/ + +class tag_srational: public tag_data_ptr + { + + private: + + const dng_srational fValue; + + public: + + tag_srational (uint16 code, + const dng_srational &value) + + : tag_data_ptr (code, ttSRational, 1, &fValue) + + , fValue (value) + + { + } + + }; + +/******************************************************************************/ + +class tag_srational_ptr: public tag_data_ptr + { + + public: + + tag_srational_ptr (uint16 code, + const dng_srational *data = NULL, + uint32 count = 1) + + : tag_data_ptr (code, ttSRational, count, data) + + { + } + + }; + +/******************************************************************************/ + +class tag_real64: public tag_data_ptr + { + + private: + + real64 fValue; + + public: + + tag_real64 (uint16 code, + real64 value = 0.0) + + : tag_data_ptr (code, ttDouble, 1, &fValue) + + , fValue (value) + + { + } + + void Set (real64 value) + { + fValue = value; + } + + }; + +/******************************************************************************/ + +class tag_matrix: public tag_srational_ptr + { + + private: + + dng_srational fEntry [kMaxColorPlanes * + kMaxColorPlanes]; + + public: + + tag_matrix (uint16 code, + const dng_matrix &m); + + }; + +/******************************************************************************/ + +class tag_icc_profile: public tag_data_ptr + { + + public: + + tag_icc_profile (const void *profileData, uint32 profileSize); + + }; + +/******************************************************************************/ + +class tag_cfa_pattern: public tiff_tag + { + + private: + + uint32 fRows; + uint32 fCols; + + const uint8 *fPattern; + + public: + + tag_cfa_pattern (uint16 code, + uint32 rows, + uint32 cols, + const uint8 *pattern) + + : tiff_tag (code, ttUndefined, 4 + rows * cols) + + , fRows (rows ) + , fCols (cols ) + , fPattern (pattern) + + { + } + + virtual void Put (dng_stream &stream) const; + + private: + + // Hidden copy constructor and assignment operator. + + tag_cfa_pattern (const tag_cfa_pattern &tag); + + tag_cfa_pattern & operator= (const tag_cfa_pattern &tag); + + }; + +/******************************************************************************/ + +class tag_exif_date_time: public tag_data_ptr + { + + private: + + char fData [20]; + + public: + + tag_exif_date_time (uint16 code, + const dng_date_time &dt); + + }; + +/******************************************************************************/ + +class tag_iptc: public tiff_tag + { + + private: + + const void *fData; + + uint32 fLength; + + public: + + tag_iptc (const void *data, + uint32 length); + + virtual void Put (dng_stream &stream) const; + + private: + + // Hidden copy constructor and assignment operator. + + tag_iptc (const tag_iptc &tag); + + tag_iptc & operator= (const tag_iptc &tag); + + }; + +/******************************************************************************/ + +class tag_xmp: public tag_uint8_ptr + { + + private: + + AutoPtr fBuffer; + + public: + + tag_xmp (const dng_xmp *xmp); + + private: + + // Hidden copy constructor and assignment operator. + + tag_xmp (const tag_xmp &tag); + + tag_xmp & operator= (const tag_xmp &tag); + + }; + +/******************************************************************************/ + +class dng_tiff_directory + { + + private: + + enum + { + kMaxEntries = 100 + }; + + uint32 fEntries; + + const tiff_tag *fTag [kMaxEntries]; + + uint32 fChained; + + public: + + dng_tiff_directory () + + : fEntries (0) + , fChained (0) + + { + } + + virtual ~dng_tiff_directory () + { + } + + void Add (const tiff_tag *tag); + + void SetChained (uint32 offset) + { + fChained = offset; + } + + uint32 Size () const; + + enum OffsetsBase + { + offsetsRelativeToStream = 0, + offsetsRelativeToExplicitBase = 1, + offsetsRelativeToIFD = 2 + }; + + void Put (dng_stream &stream, + OffsetsBase offsetsBase = offsetsRelativeToStream, + uint32 explicitBase = 0) const; + + private: + + // Hidden copy constructor and assignment operator. + + dng_tiff_directory (const dng_tiff_directory &dir); + + dng_tiff_directory & operator= (const dng_tiff_directory &dir); + + }; + +/******************************************************************************/ + +class dng_basic_tag_set + { + + private: + + tag_uint32 fNewSubFileType; + + tag_uint32 fImageWidth; + tag_uint32 fImageLength; + + tag_uint16 fPhotoInterpretation; + + tag_uint16 fFillOrder; + + tag_uint16 fSamplesPerPixel; + + uint16 fBitsPerSampleData [kMaxSamplesPerPixel]; + + tag_uint16_ptr fBitsPerSample; + + bool fStrips; + + tag_uint32 fTileWidth; + tag_uint32 fTileLength; + + dng_memory_data fTileInfoBuffer; + + uint32 *fTileOffsetData; + + tag_uint32_ptr fTileOffsets; + + uint32 *fTileByteCountData; + + tag_uint32_ptr fTileByteCounts; + + tag_uint16 fPlanarConfiguration; + + tag_uint16 fCompression; + + tag_uint16 fPredictor; + + uint16 fExtraSamplesData [kMaxSamplesPerPixel]; + + tag_uint16_ptr fExtraSamples; + + uint16 fSampleFormatData [kMaxSamplesPerPixel]; + + tag_uint16_ptr fSampleFormat; + + tag_uint16 fRowInterleaveFactor; + + uint16 fSubTileBlockSizeData [2]; + + tag_uint16_ptr fSubTileBlockSize; + + public: + + dng_basic_tag_set (dng_tiff_directory &directory, + const dng_ifd &info); + + virtual ~dng_basic_tag_set () + { + } + + void SetTileOffset (uint32 index, + uint32 offset) + { + fTileOffsetData [index] = offset; + } + + void SetTileByteCount (uint32 index, + uint32 count) + { + fTileByteCountData [index] = count; + } + + bool WritingStrips () const + { + return fStrips; + } + + private: + + // Hidden copy constructor and assignment operator. + + dng_basic_tag_set (const dng_basic_tag_set &set); + + dng_basic_tag_set & operator= (const dng_basic_tag_set &set); + + }; + +/******************************************************************************/ + +class exif_tag_set + { + + protected: + + dng_tiff_directory fExifIFD; + dng_tiff_directory fGPSIFD; + + private: + + tag_uint32 fExifLink; + tag_uint32 fGPSLink; + + bool fAddedExifLink; + bool fAddedGPSLink; + + uint8 fExifVersionData [4]; + + tag_data_ptr fExifVersion; + + tag_urational fExposureTime; + tag_srational fShutterSpeedValue; + + tag_urational fFNumber; + tag_urational fApertureValue; + + tag_srational fBrightnessValue; + + tag_srational fExposureBiasValue; + + tag_urational fMaxApertureValue; + + tag_urational fSubjectDistance; + + tag_urational fFocalLength; + + tag_uint16 fISOSpeedRatings; + + tag_uint16 fSensitivityType; + tag_uint32 fStandardOutputSensitivity; + tag_uint32 fRecommendedExposureIndex; + tag_uint32 fISOSpeed; + tag_uint32 fISOSpeedLatitudeyyy; + tag_uint32 fISOSpeedLatitudezzz; + + tag_uint16 fFlash; + + tag_uint16 fExposureProgram; + + tag_uint16 fMeteringMode; + + tag_uint16 fLightSource; + + tag_uint16 fSensingMethod; + + tag_uint16 fFocalLength35mm; + + uint8 fFileSourceData; + tag_data_ptr fFileSource; + + uint8 fSceneTypeData; + tag_data_ptr fSceneType; + + tag_cfa_pattern fCFAPattern; + + tag_uint16 fCustomRendered; + tag_uint16 fExposureMode; + tag_uint16 fWhiteBalance; + tag_uint16 fSceneCaptureType; + tag_uint16 fGainControl; + tag_uint16 fContrast; + tag_uint16 fSaturation; + tag_uint16 fSharpness; + tag_uint16 fSubjectDistanceRange; + + tag_urational fDigitalZoomRatio; + + tag_urational fExposureIndex; + + tag_uint32 fImageNumber; + + tag_uint16 fSelfTimerMode; + + tag_string fBatteryLevelA; + tag_urational fBatteryLevelR; + + tag_urational fFocalPlaneXResolution; + tag_urational fFocalPlaneYResolution; + + tag_uint16 fFocalPlaneResolutionUnit; + + uint16 fSubjectAreaData [4]; + + tag_uint16_ptr fSubjectArea; + + dng_urational fLensInfoData [4]; + + tag_urational_ptr fLensInfo; + + tag_exif_date_time fDateTime; + tag_exif_date_time fDateTimeOriginal; + tag_exif_date_time fDateTimeDigitized; + + tag_string fSubsecTime; + tag_string fSubsecTimeOriginal; + tag_string fSubsecTimeDigitized; + + tag_string fMake; + tag_string fModel; + tag_string fArtist; + tag_string fSoftware; + tag_string fCopyright; + tag_string fImageDescription; + + tag_string fSerialNumber; + + tag_uint16 fMakerNoteSafety; + + tag_data_ptr fMakerNote; + + tag_encoded_text fUserComment; + + char fImageUniqueIDData [33]; + + tag_data_ptr fImageUniqueID; + + // EXIF 2.3 tags. + + tag_string fCameraOwnerName; + tag_string fBodySerialNumber; + tag_urational_ptr fLensSpecification; + tag_string fLensMake; + tag_string fLensModel; + tag_string fLensSerialNumber; + + uint8 fGPSVersionData [4]; + + tag_uint8_ptr fGPSVersionID; + + tag_string fGPSLatitudeRef; + tag_urational_ptr fGPSLatitude; + + tag_string fGPSLongitudeRef; + tag_urational_ptr fGPSLongitude; + + tag_uint8 fGPSAltitudeRef; + tag_urational fGPSAltitude; + + tag_urational_ptr fGPSTimeStamp; + + tag_string fGPSSatellites; + tag_string fGPSStatus; + tag_string fGPSMeasureMode; + + tag_urational fGPSDOP; + + tag_string fGPSSpeedRef; + tag_urational fGPSSpeed; + + tag_string fGPSTrackRef; + tag_urational fGPSTrack; + + tag_string fGPSImgDirectionRef; + tag_urational fGPSImgDirection; + + tag_string fGPSMapDatum; + + tag_string fGPSDestLatitudeRef; + tag_urational_ptr fGPSDestLatitude; + + tag_string fGPSDestLongitudeRef; + tag_urational_ptr fGPSDestLongitude; + + tag_string fGPSDestBearingRef; + tag_urational fGPSDestBearing; + + tag_string fGPSDestDistanceRef; + tag_urational fGPSDestDistance; + + tag_encoded_text fGPSProcessingMethod; + tag_encoded_text fGPSAreaInformation; + + tag_string fGPSDateStamp; + + tag_uint16 fGPSDifferential; + + tag_urational fGPSHPositioningError; + + public: + + exif_tag_set (dng_tiff_directory &directory, + const dng_exif &exif, + bool makerNoteSafe = false, + const void *makerNoteData = NULL, + uint32 makerNoteLength = 0, + bool insideDNG = false); + + void Locate (uint32 offset) + { + fExifLink.Set (offset); + fGPSLink .Set (offset + fExifIFD.Size ()); + } + + uint32 Size () const + { + return fExifIFD.Size () + + fGPSIFD .Size (); + } + + void Put (dng_stream &stream) const + { + fExifIFD.Put (stream); + fGPSIFD .Put (stream); + } + + protected: + + void AddLinks (dng_tiff_directory &directory); + + private: + + // Hidden copy constructor and assignment operator. + + exif_tag_set (const exif_tag_set &set); + + exif_tag_set & operator= (const exif_tag_set &set); + + }; + +/******************************************************************************/ + +class tiff_dng_extended_color_profile: private dng_tiff_directory + { + + protected: + + const dng_camera_profile &fProfile; + + public: + + tiff_dng_extended_color_profile (const dng_camera_profile &profile); + + void Put (dng_stream &stream, + bool includeModelRestriction = true); + + }; + +/*****************************************************************************/ + +class tag_dng_noise_profile: public tag_data_ptr + { + + protected: + + real64 fValues [2 * kMaxColorPlanes]; + + public: + + explicit tag_dng_noise_profile (const dng_noise_profile &profile); + + }; + +/*****************************************************************************/ + +// Enum to control the subset of metadata to save to a file. + +enum dng_metadata_subset + { + + kMetadataSubset_CopyrightOnly = 0, + kMetadataSubset_CopyrightAndContact, + kMetadataSubset_AllExceptCameraInfo, + kMetadataSubset_All, + kMetadataSubset_AllExceptLocationInfo, + kMetadataSubset_AllExceptCameraAndLocation, + + kMetadataSubset_Last = kMetadataSubset_AllExceptCameraAndLocation + + }; + +/*****************************************************************************/ + +/// \brief Support for writing dng_image or dng_negative instances to a +/// dng_stream in TIFF or DNG format. + +class dng_image_writer + { + + bool fComputeMd5Sum; // Set to true to compute md5sum + + friend class dng_jpeg_image; + friend class dng_jpeg_image_encode_task; + friend class dng_write_tiles_task; + + protected: + + enum + { + + // Target size for buffer used to copy data to the image. + + kImageBufferSize = 128 * 1024 + + }; + + public: + + dng_image_writer (); + + virtual ~dng_image_writer (); + +#if GPR_WRITING + virtual uint32 GetDefaultCompression() { return ccJPEG; } +#endif + + void SetComputeMd5Sum(bool x) { fComputeMd5Sum = x; } + + virtual void EncodeJPEGPreview (dng_host &host, + const dng_image &image, + dng_jpeg_preview &preview, + int32 quality = -1); + + virtual void WriteImage (dng_host &host, + const dng_ifd &ifd, + dng_basic_tag_set &basic, + dng_stream &stream, + const dng_image &image, + uint32 fakeChannels = 1); + + /// Write a dng_image to a dng_stream in TIFF format. + /// \param host Host interface used for progress updates, abort testing, buffer allocation, etc. + /// \param stream The dng_stream on which to write the TIFF. + /// \param image The actual image data to be written. + /// \param photometricInterpretation Either piBlackIsZero for monochrome or piRGB for RGB images. + /// \param compression Must be ccUncompressed. + /// \param negative or metadata If non-NULL, EXIF, IPTC, and XMP metadata from this negative is written to TIFF. + /// \param space If non-null and color space has an ICC profile, TIFF will be tagged with this + /// profile. No color space conversion of image data occurs. + /// \param resolution If non-NULL, TIFF will be tagged with this resolution. + /// \param thumbnail If non-NULL, will be stored in TIFF as preview image. + /// \param imageResources If non-NULL, will image resources be stored in TIFF as well. + /// \param metadataSubset The subset of metadata (e.g., copyright only) to include in the TIFF. + + void WriteTIFF (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation, + uint32 compression, + dng_negative *negative, + const dng_color_space *space = NULL, + const dng_resolution *resolution = NULL, + const dng_jpeg_preview *thumbnail = NULL, + const dng_memory_block *imageResources = NULL, + dng_metadata_subset metadataSubset = kMetadataSubset_All); + + void WriteTIFF (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation = piBlackIsZero, + uint32 compression = ccUncompressed, + const dng_metadata *metadata = NULL, + const dng_color_space *space = NULL, + const dng_resolution *resolution = NULL, + const dng_jpeg_preview *thumbnail = NULL, + const dng_memory_block *imageResources = NULL, + dng_metadata_subset metadataSubset = kMetadataSubset_All); + + /// Write a dng_image to a dng_stream in TIFF format. + /// \param host Host interface used for progress updates, abort testing, buffer allocation, etc. + /// \param stream The dng_stream on which to write the TIFF. + /// \param image The actual image data to be written. + /// \param photometricInterpretation Either piBlackIsZero for monochrome or piRGB for RGB images. + /// \param compression Must be ccUncompressed. + /// \param negative or metadata If non-NULL, EXIF, IPTC, and XMP metadata from this negative is written to TIFF. + /// \param profileData If non-null, TIFF will be tagged with this profile. No color space conversion + /// of image data occurs. + /// \param profileSize The size for the profile data. + /// \param resolution If non-NULL, TIFF will be tagged with this resolution. + /// \param thumbnail If non-NULL, will be stored in TIFF as preview image. + /// \param imageResources If non-NULL, will image resources be stored in TIFF as well. + /// \param metadataSubset The subset of metadata (e.g., copyright only) to include in the TIFF. + + void WriteTIFFWithProfile (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation, + uint32 compression, + dng_negative *negative, + const void *profileData = NULL, + uint32 profileSize = 0, + const dng_resolution *resolution = NULL, + const dng_jpeg_preview *thumbnail = NULL, + const dng_memory_block *imageResources = NULL, + dng_metadata_subset metadataSubset = kMetadataSubset_All); + + virtual void WriteTIFFWithProfile (dng_host &host, + dng_stream &stream, + const dng_image &image, + uint32 photometricInterpretation = piBlackIsZero, + uint32 compression = ccUncompressed, + const dng_metadata *metadata = NULL, + const void *profileData = NULL, + uint32 profileSize = 0, + const dng_resolution *resolution = NULL, + const dng_jpeg_preview *thumbnail = NULL, + const dng_memory_block *imageResources = NULL, + dng_metadata_subset metadataSubset = kMetadataSubset_All); + + /// Write a dng_image to a dng_stream in DNG format. + /// \param host Host interface used for progress updates, abort testing, buffer allocation, etc. + /// \param stream The dng_stream on which to write the TIFF. + /// \param negative The image data and metadata (EXIF, IPTC, XMP) to be written. + /// \param previewList List of previews (not counting thumbnail) to write to the file. Defaults to empty. + /// \param maxBackwardVersion The DNG file should be readable by readers at least back to this version. + /// \param uncompressed True to force uncompressed images. Otherwise use normal compression. + + void WriteDNG (dng_host &host, + dng_stream &stream, + dng_negative &negative, + const dng_preview_list *previewList = NULL, + uint32 maxBackwardVersion = dngVersion_SaveDefault, + bool uncompressed = false); + + /// Write a dng_image to a dng_stream in DNG format. + /// \param host Host interface used for progress updates, abort testing, buffer allocation, etc. + /// \param stream The dng_stream on which to write the TIFF. + /// \param negative The image data to be written. + /// \param metadata The metadata (EXIF, IPTC, XMP) to be written. + /// \param previewList List of previews (not counting thumbnail) to write to the file. Defaults to empty. + /// \param maxBackwardVersion The DNG file should be readable by readers at least back to this version. + /// \param uncompressed True to force uncompressed images. Otherwise use normal compression. + + virtual void WriteDNG (dng_host &host, + dng_stream &stream, + const dng_negative &negative, + const dng_metadata &metadata, + const dng_preview_list *previewList = NULL, + uint32 maxBackwardVersion = dngVersion_SaveDefault, + bool uncompressed = false); + + /// Resolve metadata conflicts and apply metadata policies in keeping + /// with Metadata Working Group (MWG) guidelines. + + virtual void CleanUpMetadata (dng_host &host, + dng_metadata &metadata, + dng_metadata_subset metadataSubset, + const char *dstMIMI, + const char *software = NULL); + + protected: + + virtual uint32 CompressedBufferSize (const dng_ifd &ifd, + uint32 uncompressedSize); + + virtual void EncodePredictor (dng_host &host, + const dng_ifd &ifd, + dng_pixel_buffer &buffer, + AutoPtr &tempBuffer); + + virtual void ByteSwapBuffer (dng_host &host, + dng_pixel_buffer &buffer); + + void ReorderSubTileBlocks (const dng_ifd &ifd, + dng_pixel_buffer &buffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer); + + virtual void WriteData (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_pixel_buffer &buffer, + AutoPtr &compressedBuffer); + + virtual void WriteTile (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + const dng_image &image, + const dng_rect &tileArea, + uint32 fakeChannels, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer, + AutoPtr &tempBuffer); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_info.cpp b/source/lib/dng_sdk/dng_info.cpp new file mode 100644 index 0000000..cf4a599 --- /dev/null +++ b/source/lib/dng_sdk/dng_info.cpp @@ -0,0 +1,2529 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_info.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_info.h" + +#include "dng_camera_profile.h" +#include "dng_exceptions.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_tag_codes.h" +#include "dng_parse_utils.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_info::dng_info () + + : fTIFFBlockOffset (0) + , fTIFFBlockOriginalOffset (kDNGStreamInvalidOffset) + , fBigEndian (false) + , fMagic (0) + , fExif () + , fShared () + , fMainIndex (-1) + , fMaskIndex (-1) + , fIFDCount (0) + , fChainedIFDCount (0) + , fMakerNoteNextIFD (0) + + { + + } + +/*****************************************************************************/ + +dng_info::~dng_info () + { + + } + +/*****************************************************************************/ + +void dng_info::ValidateMagic () + { + + switch (fMagic) + { + + case magicTIFF: + case magicExtendedProfile: + case magicRawCache: + case magicPanasonic: + case magicOlympusA: + case magicOlympusB: + { + + return; + + } + + default: + { + + #if qDNGValidate + + ReportError ("Invalid TIFF magic number"); + + #endif + + ThrowBadFormat (); + + } + + } + + } + +/*****************************************************************************/ + +void dng_info::ParseTag (dng_host &host, + dng_stream &stream, + dng_exif *exif, + dng_shared *shared, + dng_ifd *ifd, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset, + int64 offsetDelta) + { + + bool isSubIFD = parentCode >= tcFirstSubIFD && + parentCode <= tcLastSubIFD; + + bool isMainIFD = (parentCode == 0 || isSubIFD) && + ifd && + ifd->fUsesNewSubFileType && + ifd->fNewSubFileType == sfMainImage; + + // Panasonic RAW format stores private tags using tag codes < 254 in + // IFD 0. Redirect the parsing of these tags into a logical + // "PanasonicRAW" IFD. + + // Panasonic is starting to use some higher numbers also (280..283). + + if (fMagic == 85 && parentCode == 0 && (tagCode < tcNewSubFileType || + (tagCode >= 280 && tagCode <= 283))) + { + + parentCode = tcPanasonicRAW; + + ifd = NULL; + + } + + stream.SetReadPosition (tagOffset); + + if (ifd && ifd->ParseTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return; + + } + + stream.SetReadPosition (tagOffset); + + if (exif && shared && exif->ParseTag (stream, + *shared, + parentCode, + isMainIFD, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return; + + } + + stream.SetReadPosition (tagOffset); + + if (shared && exif && shared->ParseTag (stream, + *exif, + parentCode, + isMainIFD, + tagCode, + tagType, + tagCount, + tagOffset, + offsetDelta)) + { + + return; + + } + + if (parentCode == tcLeicaMakerNote && + tagType == ttUndefined && + tagCount >= 14) + { + + if (ParseMakerNoteIFD (host, + stream, + tagCount, + tagOffset, + offsetDelta, + tagOffset, + stream.Length (), + tcLeicaMakerNote)) + { + + return; + + } + + } + + if (parentCode == tcOlympusMakerNote && + tagType == ttUndefined && + tagCount >= 14) + { + + uint32 olympusMakerParent = 0; + + switch (tagCode) + { + + case 8208: + olympusMakerParent = tcOlympusMakerNote8208; + break; + + case 8224: + olympusMakerParent = tcOlympusMakerNote8224; + break; + + case 8240: + olympusMakerParent = tcOlympusMakerNote8240; + break; + + case 8256: + olympusMakerParent = tcOlympusMakerNote8256; + break; + + case 8272: + olympusMakerParent = tcOlympusMakerNote8272; + break; + + case 12288: + olympusMakerParent = tcOlympusMakerNote12288; + break; + + default: + break; + + } + + if (olympusMakerParent) + { + + // Olympus made a mistake in some camera models in computing + // the size of these sub-tags, so we fudge the count. + + if (ParseMakerNoteIFD (host, + stream, + stream.Length () - tagOffset, + tagOffset, + offsetDelta, + tagOffset, + stream.Length (), + olympusMakerParent)) + { + + return; + + } + + } + + } + + if (parentCode == tcRicohMakerNote && + tagCode == 0x2001 && + tagType == ttUndefined && + tagCount > 22) + { + + char header [20]; + + stream.SetReadPosition (tagOffset); + + stream.Get (header, sizeof (header)); + + if (memcmp (header, "[Ricoh Camera Info]", 19) == 0) + { + + ParseMakerNoteIFD (host, + stream, + tagCount - 20, + tagOffset + 20, + offsetDelta, + tagOffset + 20, + tagOffset + tagCount, + tcRicohMakerNoteCameraInfo); + + return; + + } + + } + + #if qDNGValidate + + { + + stream.SetReadPosition (tagOffset); + + if (gVerbose) + { + + printf ("*"); + + DumpTagValues (stream, + LookupTagType (tagType), + parentCode, + tagCode, + tagType, + tagCount); + + } + + // If type is ASCII, then parse anyway so we report any ASCII + // NULL termination or character set errors. + + else if (tagType == ttAscii) + { + + dng_string s; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + s, + false); + + } + + } + + #endif + + } + +/*****************************************************************************/ + +bool dng_info::ValidateIFD (dng_stream &stream, + uint64 ifdOffset, + int64 offsetDelta) + { + + // Make sure we have a count. + + if (ifdOffset + 2 > stream.Length ()) + { + return false; + } + + // Get entry count. + + stream.SetReadPosition (ifdOffset); + + uint32 ifdEntries = stream.Get_uint16 (); + + if (ifdEntries < 1) + { + return false; + } + + // Make sure we have room for all entries and next IFD link. + + if (ifdOffset + 2 + ifdEntries * 12 + 4 > stream.Length ()) + { + return false; + } + + // Check each entry. + + for (uint32 tag_index = 0; tag_index < ifdEntries; tag_index++) + { + + stream.SetReadPosition (ifdOffset + 2 + tag_index * 12); + + stream.Skip (2); // Ignore tag code. + + uint32 tagType = stream.Get_uint16 (); + uint32 tagCount = stream.Get_uint32 (); + + uint32 tag_type_size = TagTypeSize (tagType); + + if (tag_type_size == 0) + { + return false; + } + + uint32 tag_data_size = tagCount * tag_type_size; + + if (tag_data_size > 4) + { + + uint64 tagOffset = stream.Get_uint32 (); + + tagOffset += offsetDelta; + + if (tagOffset + tag_data_size > stream.Length ()) + { + return false; + } + + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_info::ParseIFD (dng_host &host, + dng_stream &stream, + dng_exif *exif, + dng_shared *shared, + dng_ifd *ifd, + uint64 ifdOffset, + int64 offsetDelta, + uint32 parentCode) + { + + #if qDNGValidate + + bool isMakerNote = (parentCode >= tcFirstMakerNoteIFD && + parentCode <= tcLastMakerNoteIFD); + + #endif + + stream.SetReadPosition (ifdOffset); + + if (ifd) + { + ifd->fThisIFD = ifdOffset; + } + + uint32 ifdEntries = stream.Get_uint16 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: Offset = %u, Entries = %u\n\n", + LookupParentCode (parentCode), + (unsigned) ifdOffset, + (unsigned) ifdEntries); + + } + + if ((ifdOffset & 1) && !isMakerNote) + { + + char message [256]; + + sprintf (message, + "%s has odd offset (%u)", + LookupParentCode (parentCode), + (unsigned) ifdOffset); + + ReportWarning (message); + + } + + #endif + + uint32 prev_tag_code = 0; + + for (uint32 tag_index = 0; tag_index < ifdEntries; tag_index++) + { + + stream.SetReadPosition (ifdOffset + 2 + tag_index * 12); + + uint32 tagCode = stream.Get_uint16 (); + uint32 tagType = stream.Get_uint16 (); + + // Minolta 7D files have a bug in the EXIF block where the count + // is wrong, and we run off into next IFD link. So if abort parsing + // if we get a zero code/type combinations. + + if (tagCode == 0 && tagType == 0) + { + + #if qDNGValidate + + char message [256]; + + sprintf (message, + "%s had zero/zero tag code/type entry", + LookupParentCode (parentCode)); + + ReportWarning (message); + + #endif + + return; + + } + + uint32 tagCount = stream.Get_uint32 (); + + #if qDNGValidate + + { + + if (tag_index > 0 && tagCode <= prev_tag_code && !isMakerNote) + { + + char message [256]; + + sprintf (message, + "%s tags are not sorted in ascending numerical order", + LookupParentCode (parentCode)); + + ReportWarning (message); + + } + + } + + #endif + + prev_tag_code = tagCode; + + uint32 tag_type_size = TagTypeSize (tagType); + + if (tag_type_size == 0) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s has unknown type (%u)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode), + (unsigned) tagType); + + ReportWarning (message); + + } + + #endif + + continue; + + } + + uint64 tagOffset = ifdOffset + 2 + tag_index * 12 + 8; + + if (tagCount * tag_type_size > 4) + { + + tagOffset = stream.Get_uint32 (); + + #if qDNGValidate + + { + + if (!(ifdOffset & 1) && + (tagOffset & 1) && + !isMakerNote && + parentCode != tcKodakDCRPrivateIFD && + parentCode != tcKodakKDCPrivateIFD) + { + + char message [256]; + + sprintf (message, + "%s %s has odd data offset (%u)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode), + (unsigned) tagOffset); + + ReportWarning (message); + + } + + } + + #endif + + tagOffset += offsetDelta; + + stream.SetReadPosition (tagOffset); + + } + + ParseTag (host, + stream, + exif, + shared, + ifd, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset, + offsetDelta); + + } + + stream.SetReadPosition (ifdOffset + 2 + ifdEntries * 12); + + uint32 nextIFD = stream.Get_uint32 (); + + #if qDNGValidate + + if (gVerbose) + { + printf ("NextIFD = %u\n", (unsigned) nextIFD); + } + + #endif + + if (ifd) + { + ifd->fNextIFD = nextIFD; + } + + #if qDNGValidate + + if (nextIFD) + { + + if (parentCode != 0 && + (parentCode < tcFirstChainedIFD || + parentCode > tcLastChainedIFD )) + { + + char message [256]; + + sprintf (message, + "%s has an unexpected non-zero NextIFD (%u)", + LookupParentCode (parentCode), + (unsigned) nextIFD); + + ReportWarning (message); + + } + + } + + if (gVerbose) + { + printf ("\n"); + } + + #endif + + } + +/*****************************************************************************/ + +bool dng_info::ParseMakerNoteIFD (dng_host &host, + dng_stream &stream, + uint64 ifdSize, + uint64 ifdOffset, + int64 offsetDelta, + uint64 minOffset, + uint64 maxOffset, + uint32 parentCode) + { + + uint32 tagIndex; + uint32 tagCode; + uint32 tagType; + uint32 tagCount; + + // Assume there is no next IFD pointer. + + fMakerNoteNextIFD = 0; + + // If size is too small to hold a single entry IFD, abort. + + if (ifdSize < 14) + { + return false; + } + + // Get entry count. + + stream.SetReadPosition (ifdOffset); + + uint32 ifdEntries = stream.Get_uint16 (); + + // Make the entry count if reasonable for the MakerNote size. + + if (ifdEntries < 1 || 2 + ifdEntries * 12 > ifdSize) + { + return false; + } + + // Scan IFD to verify all the tag types are all valid. + + for (tagIndex = 0; tagIndex < ifdEntries; tagIndex++) + { + + stream.SetReadPosition (ifdOffset + 2 + tagIndex * 12 + 2); + + tagType = stream.Get_uint16 (); + + // Kludge: Some Canon MakerNotes contain tagType = 0 tags, so we + // need to ignore them. This was a "firmware 1.0.4" Canon 40D raw file. + + if (parentCode == tcCanonMakerNote && tagType == 0) + { + continue; + } + + if (TagTypeSize (tagType) == 0) + { + return false; + } + + } + + // OK, the IFD looks reasonable enough to parse. + + #if qDNGValidate + + if (gVerbose) + { + + printf ("%s: Offset = %u, Entries = %u\n\n", + LookupParentCode (parentCode), + (unsigned) ifdOffset, + (unsigned) ifdEntries); + + } + + #endif + + for (tagIndex = 0; tagIndex < ifdEntries; tagIndex++) + { + + stream.SetReadPosition (ifdOffset + 2 + tagIndex * 12); + + tagCode = stream.Get_uint16 (); + tagType = stream.Get_uint16 (); + tagCount = stream.Get_uint32 (); + + if (tagType == 0) + { + continue; + } + + uint32 tagSize = tagCount * TagTypeSize (tagType); + + uint64 tagOffset = ifdOffset + 2 + tagIndex * 12 + 8; + + if (tagSize > 4) + { + + tagOffset = stream.Get_uint32 () + offsetDelta; + + if (tagOffset < minOffset || + tagOffset + tagSize > maxOffset) + { + + // Tag data is outside the valid offset range, + // so ignore this tag. + + continue; + + } + + stream.SetReadPosition (tagOffset); + + } + + // Olympus switched to using IFDs in version 3 makernotes. + + if (parentCode == tcOlympusMakerNote && + tagType == ttIFD && + tagCount == 1) + { + + uint32 olympusMakerParent = 0; + + switch (tagCode) + { + + case 8208: + olympusMakerParent = tcOlympusMakerNote8208; + break; + + case 8224: + olympusMakerParent = tcOlympusMakerNote8224; + break; + + case 8240: + olympusMakerParent = tcOlympusMakerNote8240; + break; + + case 8256: + olympusMakerParent = tcOlympusMakerNote8256; + break; + + case 8272: + olympusMakerParent = tcOlympusMakerNote8272; + break; + + case 12288: + olympusMakerParent = tcOlympusMakerNote12288; + break; + + default: + break; + + } + + if (olympusMakerParent) + { + + stream.SetReadPosition (tagOffset); + + uint64 subMakerNoteOffset = stream.Get_uint32 () + offsetDelta; + + if (subMakerNoteOffset >= minOffset && + subMakerNoteOffset < maxOffset) + { + + if (ParseMakerNoteIFD (host, + stream, + maxOffset - subMakerNoteOffset, + subMakerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + olympusMakerParent)) + { + + continue; + + } + + } + + } + + stream.SetReadPosition (tagOffset); + + } + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset, + offsetDelta); + + } + + // Grab next IFD pointer, for possible use. + + if (ifdSize >= 2 + ifdEntries * 12 + 4) + { + + stream.SetReadPosition (ifdOffset + 2 + ifdEntries * 12); + + fMakerNoteNextIFD = stream.Get_uint32 (); + + } + + #if qDNGValidate + + if (gVerbose) + { + printf ("\n"); + } + + #endif + + return true; + + } + +/*****************************************************************************/ + +void dng_info::ParseMakerNote (dng_host &host, + dng_stream &stream, + uint32 makerNoteCount, + uint64 makerNoteOffset, + int64 offsetDelta, + uint64 minOffset, + uint64 maxOffset) + { + + uint8 firstBytes [16]; + + memset (firstBytes, 0, sizeof (firstBytes)); + + stream.SetReadPosition (makerNoteOffset); + + stream.Get (firstBytes, (uint32) Min_uint64 (sizeof (firstBytes), + makerNoteCount)); + + // Epson MakerNote with header. + + if (memcmp (firstBytes, "EPSON\000\001\000", 8) == 0) + { + + if (makerNoteCount > 8) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 8, + makerNoteOffset + 8, + offsetDelta, + minOffset, + maxOffset, + tcEpsonMakerNote); + + } + + return; + + } + + // Fujifilm MakerNote. + + if (memcmp (firstBytes, "FUJIFILM", 8) == 0) + { + + stream.SetReadPosition (makerNoteOffset + 8); + + TempLittleEndian tempEndian (stream); + + uint32 ifd_offset = stream.Get_uint32 (); + + if (ifd_offset >= 12 && ifd_offset < makerNoteCount) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - ifd_offset, + makerNoteOffset + ifd_offset, + makerNoteOffset, + minOffset, + maxOffset, + tcFujiMakerNote); + + } + + return; + + } + + // Leica MakerNote for models that store entry offsets relative to the start of + // the MakerNote (e.g., M9). + + if ((memcmp (firstBytes, "LEICA\000\000\000", 8) == 0) || + (memcmp (firstBytes, "LEICA0\003\000", 8) == 0) || + (memcmp (firstBytes, "LEICA\000\001\000", 8) == 0) || + (memcmp (firstBytes, "LEICA\000\005\000", 8) == 0)) + { + + if (makerNoteCount > 8) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 8, + makerNoteOffset + 8, + makerNoteOffset, + minOffset, + maxOffset, + tcLeicaMakerNote); + + } + + return; + + } + + // Leica MakerNote for models that store absolute entry offsets (i.e., relative + // to the start of the file, e.g., S2). + + if (memcmp (firstBytes, "LEICA\000\002\377", 8) == 0) + { + + if (makerNoteCount > 8) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 8, + makerNoteOffset + 8, + offsetDelta, + minOffset, + maxOffset, + tcLeicaMakerNote); + + } + + return; + + } + + // Nikon version 2 MakerNote with header. + + if (memcmp (firstBytes, "Nikon\000\002", 7) == 0) + { + + stream.SetReadPosition (makerNoteOffset + 10); + + bool bigEndian = false; + + uint16 endianMark = stream.Get_uint16 (); + + if (endianMark == byteOrderMM) + { + bigEndian = true; + } + + else if (endianMark != byteOrderII) + { + return; + } + + TempBigEndian temp_endian (stream, bigEndian); + + uint16 magic = stream.Get_uint16 (); + + if (magic != 42) + { + return; + } + + uint32 ifd_offset = stream.Get_uint32 (); + + if (ifd_offset >= 8 && ifd_offset < makerNoteCount - 10) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 10 - ifd_offset, + makerNoteOffset + 10 + ifd_offset, + makerNoteOffset + 10, + minOffset, + maxOffset, + tcNikonMakerNote); + + } + + return; + + } + + // Newer version of Olympus MakerNote with byte order mark. + + if (memcmp (firstBytes, "OLYMPUS\000", 8) == 0) + { + + stream.SetReadPosition (makerNoteOffset + 8); + + bool bigEndian = false; + + uint16 endianMark = stream.Get_uint16 (); + + if (endianMark == byteOrderMM) + { + bigEndian = true; + } + + else if (endianMark != byteOrderII) + { + return; + } + + TempBigEndian temp_endian (stream, bigEndian); + + uint16 version = stream.Get_uint16 (); + + if (version != 3) + { + return; + } + + if (makerNoteCount > 12) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 12, + makerNoteOffset + 12, + makerNoteOffset, + minOffset, + maxOffset, + tcOlympusMakerNote); + + } + + return; + + } + + // Olympus MakerNote with header. + + if (memcmp (firstBytes, "OLYMP", 5) == 0) + { + + if (makerNoteCount > 8) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 8, + makerNoteOffset + 8, + offsetDelta, + minOffset, + maxOffset, + tcOlympusMakerNote); + + } + + return; + + } + + // Panasonic MakerNote. + + if (memcmp (firstBytes, "Panasonic\000\000\000", 12) == 0) + { + + if (makerNoteCount > 12) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 12, + makerNoteOffset + 12, + offsetDelta, + minOffset, + maxOffset, + tcPanasonicMakerNote); + + } + + return; + + } + + // Pentax MakerNote. + + if (memcmp (firstBytes, "AOC", 4) == 0) + { + + if (makerNoteCount > 6) + { + + stream.SetReadPosition (makerNoteOffset + 4); + + bool bigEndian = stream.BigEndian (); + + uint16 endianMark = stream.Get_uint16 (); + + if (endianMark == byteOrderMM) + { + bigEndian = true; + } + + else if (endianMark == byteOrderII) + { + bigEndian = false; + } + + TempBigEndian temp_endian (stream, bigEndian); + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 6, + makerNoteOffset + 6, + offsetDelta, + minOffset, + maxOffset, + tcPentaxMakerNote); + + } + + return; + + } + + // Ricoh MakerNote. + + if (memcmp (firstBytes, "RICOH", 5) == 0 || + memcmp (firstBytes, "Ricoh", 5) == 0) + { + + if (makerNoteCount > 8) + { + + TempBigEndian tempEndian (stream); + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 8, + makerNoteOffset + 8, + offsetDelta, + minOffset, + maxOffset, + tcRicohMakerNote); + + } + + return; + + } + + // Nikon MakerNote without header. + + if (fExif->fMake.StartsWith ("NIKON")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcNikonMakerNote); + + return; + + } + + // Canon MakerNote. + + if (fExif->fMake.StartsWith ("CANON")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcCanonMakerNote); + + return; + + } + + // Minolta MakerNote. + + if (fExif->fMake.StartsWith ("MINOLTA" ) || + fExif->fMake.StartsWith ("KONICA MINOLTA")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcMinoltaMakerNote); + + return; + + } + + // Sony MakerNote. + + if (fExif->fMake.StartsWith ("SONY")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcSonyMakerNote); + + return; + + } + + // Kodak MakerNote. + + if (fExif->fMake.StartsWith ("EASTMAN KODAK")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcKodakMakerNote); + + return; + + } + + // Mamiya MakerNote. + + if (fExif->fMake.StartsWith ("Mamiya")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcMamiyaMakerNote); + + // Mamiya uses a MakerNote chain. + + while (fMakerNoteNextIFD) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + offsetDelta + fMakerNoteNextIFD, + offsetDelta, + minOffset, + maxOffset, + tcMamiyaMakerNote); + + } + + return; + + } + + // Nikon MakerNote without header. + + if (fExif->fMake.StartsWith ("Hasselblad")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + offsetDelta, + minOffset, + maxOffset, + tcHasselbladMakerNote); + + return; + + } + + // Samsung MakerNote. + + if (fExif->fMake.StartsWith ("Samsung")) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount, + makerNoteOffset, + makerNoteOffset, + minOffset, + maxOffset, + tcSamsungMakerNote); + + return; + + } + + // Casio MakerNote. + + if (fExif->fMake.StartsWith ("CASIO COMPUTER") && + memcmp (firstBytes, "QVC\000\000\000", 6) == 0) + { + + ParseMakerNoteIFD (host, + stream, + makerNoteCount - 6, + makerNoteOffset + 6, + makerNoteOffset, + minOffset, + maxOffset, + tcCasioMakerNote); + + return; + + } + + } + +/*****************************************************************************/ + +void dng_info::ParseSonyPrivateData (dng_host & /* host */, + dng_stream & /* stream */, + uint64 /* count */, + uint64 /* oldOffset */, + uint64 /* newOffset */) + { + + // Sony private data is encrypted, sorry. + + } + +/*****************************************************************************/ + +void dng_info::ParseDNGPrivateData (dng_host &host, + dng_stream &stream) + { + + if (fShared->fDNGPrivateDataCount < 2) + { + return; + } + + // DNG private data should always start with a null-terminated + // company name, to define the format of the private data. + + dng_string privateName; + + { + + char buffer [64]; + + stream.SetReadPosition (fShared->fDNGPrivateDataOffset); + + uint32 readLength = Min_uint32 (fShared->fDNGPrivateDataCount, + sizeof (buffer) - 1); + + stream.Get (buffer, readLength); + + buffer [readLength] = 0; + + privateName.Set (buffer); + + } + + if (privateName.StartsWith ("GoPro" ) ) + { + +#if qDNGValidate + + if (gVerbose) + { + printf ("Parsing GoPro DNGPrivateData\n\n"); + } +#endif + + if( fShared->fDNGPrivateDataCount > 0 ) + { + host.GetGPMFPayload().Reset (host.Allocate( fShared->fDNGPrivateDataCount + 1 ) ); + + stream.SetReadPosition (fShared->fDNGPrivateDataOffset); + + stream.Get ( host.GetGPMFPayload().Get()->Buffer(), fShared->fDNGPrivateDataCount ); + } + + return; + + } + + // Pentax is storing their MakerNote in the DNGPrivateData data. + + if (privateName.StartsWith ("PENTAX" ) || + privateName.StartsWith ("SAMSUNG")) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Parsing Pentax/Samsung DNGPrivateData\n\n"); + } + + #endif + + stream.SetReadPosition (fShared->fDNGPrivateDataOffset + 8); + + bool bigEndian = stream.BigEndian (); + + uint16 endianMark = stream.Get_uint16 (); + + if (endianMark == byteOrderMM) + { + bigEndian = true; + } + + else if (endianMark == byteOrderII) + { + bigEndian = false; + } + + TempBigEndian temp_endian (stream, bigEndian); + + ParseMakerNoteIFD (host, + stream, + fShared->fDNGPrivateDataCount - 10, + fShared->fDNGPrivateDataOffset + 10, + fShared->fDNGPrivateDataOffset, + fShared->fDNGPrivateDataOffset, + fShared->fDNGPrivateDataOffset + fShared->fDNGPrivateDataCount, + tcPentaxMakerNote); + + return; + + } + + // Stop parsing if this is not an Adobe format block. + + if (!privateName.Matches ("Adobe")) + { + return; + } + + TempBigEndian temp_order (stream); + + uint32 section_offset = 6; + + while (section_offset + 8 < fShared->fDNGPrivateDataCount) + { + + stream.SetReadPosition (fShared->fDNGPrivateDataOffset + section_offset); + + uint32 section_key = stream.Get_uint32 (); + uint32 section_count = stream.Get_uint32 (); + + if (section_key == DNG_CHAR4 ('M','a','k','N') && section_count > 6) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Found MakerNote inside DNGPrivateData\n\n"); + } + + #endif + + uint16 order_mark = stream.Get_uint16 (); + uint64 old_offset = stream.Get_uint32 (); + + uint32 tempSize = section_count - 6; + + AutoPtr tempBlock (host.Allocate (tempSize)); + + uint64 positionInOriginalFile = stream.PositionInOriginalFile(); + + stream.Get (tempBlock->Buffer (), tempSize); + + dng_stream tempStream (tempBlock->Buffer (), + tempSize, + positionInOriginalFile); + + tempStream.SetBigEndian (order_mark == byteOrderMM); + + ParseMakerNote (host, + tempStream, + tempSize, + 0, + 0 - old_offset, + 0, + tempSize); + + } + + else if (section_key == DNG_CHAR4 ('S','R','2',' ') && section_count > 6) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Found Sony private data inside DNGPrivateData\n\n"); + } + + #endif + + uint16 order_mark = stream.Get_uint16 (); + uint64 old_offset = stream.Get_uint32 (); + + uint64 new_offset = fShared->fDNGPrivateDataOffset + section_offset + 14; + + TempBigEndian sr2_order (stream, order_mark == byteOrderMM); + + ParseSonyPrivateData (host, + stream, + section_count - 6, + old_offset, + new_offset); + + } + + else if (section_key == DNG_CHAR4 ('R','A','F',' ') && section_count > 4) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Found Fuji RAF tags inside DNGPrivateData\n\n"); + } + + #endif + + uint16 order_mark = stream.Get_uint16 (); + + uint32 tagCount = stream.Get_uint32 (); + + uint64 tagOffset = stream.Position (); + + if (tagCount) + { + + TempBigEndian raf_order (stream, order_mark == byteOrderMM); + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + tcFujiRAF, + tcFujiHeader, + ttUndefined, + tagCount, + tagOffset, + 0); + + stream.SetReadPosition (tagOffset + tagCount); + + } + + tagCount = stream.Get_uint32 (); + + tagOffset = stream.Position (); + + if (tagCount) + { + + TempBigEndian raf_order (stream, order_mark == byteOrderMM); + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + tcFujiRAF, + tcFujiRawInfo1, + ttUndefined, + tagCount, + tagOffset, + 0); + + stream.SetReadPosition (tagOffset + tagCount); + + } + + tagCount = stream.Get_uint32 (); + + tagOffset = stream.Position (); + + if (tagCount) + { + + TempBigEndian raf_order (stream, order_mark == byteOrderMM); + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + tcFujiRAF, + tcFujiRawInfo2, + ttUndefined, + tagCount, + tagOffset, + 0); + + stream.SetReadPosition (tagOffset + tagCount); + + } + + } + + else if (section_key == DNG_CHAR4 ('C','n','t','x') && section_count > 4) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Found Contax Raw header inside DNGPrivateData\n\n"); + } + + #endif + + uint16 order_mark = stream.Get_uint16 (); + + uint32 tagCount = stream.Get_uint32 (); + + uint64 tagOffset = stream.Position (); + + if (tagCount) + { + + TempBigEndian contax_order (stream, order_mark == byteOrderMM); + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + tcContaxRAW, + tcContaxHeader, + ttUndefined, + tagCount, + tagOffset, + 0); + + } + + } + + else if (section_key == DNG_CHAR4 ('C','R','W',' ') && section_count > 4) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Found Canon CRW tags inside DNGPrivateData\n\n"); + } + + #endif + + uint16 order_mark = stream.Get_uint16 (); + uint32 entries = stream.Get_uint16 (); + + uint64 crwTagStart = stream.Position (); + + for (uint32 parsePass = 1; parsePass <= 2; parsePass++) + { + + stream.SetReadPosition (crwTagStart); + + for (uint32 index = 0; index < entries; index++) + { + + uint32 tagCode = stream.Get_uint16 (); + + uint32 tagCount = stream.Get_uint32 (); + + uint64 tagOffset = stream.Position (); + + // We need to grab the model id tag first, and then all the + // other tags. + + if ((parsePass == 1) == (tagCode == 0x5834)) + { + + TempBigEndian tag_order (stream, order_mark == byteOrderMM); + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + tcCanonCRW, + tagCode, + ttUndefined, + tagCount, + tagOffset, + 0); + + } + + stream.SetReadPosition (tagOffset + tagCount); + + } + + } + + } + + else if (section_count > 4) + { + + uint32 parentCode = 0; + + bool code32 = false; + bool hasType = true; + + switch (section_key) + { + + case DNG_CHAR4 ('M','R','W',' '): + { + parentCode = tcMinoltaMRW; + code32 = true; + hasType = false; + break; + } + + case DNG_CHAR4 ('P','a','n','o'): + { + parentCode = tcPanasonicRAW; + break; + } + + case DNG_CHAR4 ('L','e','a','f'): + { + parentCode = tcLeafMOS; + break; + } + + case DNG_CHAR4 ('K','o','d','a'): + { + parentCode = tcKodakDCRPrivateIFD; + break; + } + + case DNG_CHAR4 ('K','D','C',' '): + { + parentCode = tcKodakKDCPrivateIFD; + break; + } + + default: + break; + + } + + if (parentCode) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("Found %s tags inside DNGPrivateData\n\n", + LookupParentCode (parentCode)); + } + + #endif + + uint16 order_mark = stream.Get_uint16 (); + uint32 entries = stream.Get_uint16 (); + + for (uint32 index = 0; index < entries; index++) + { + + uint32 tagCode = code32 ? stream.Get_uint32 () + : stream.Get_uint16 (); + + uint32 tagType = hasType ? stream.Get_uint16 () + : ttUndefined; + + uint32 tagCount = stream.Get_uint32 (); + + uint32 tagSize = tagCount * TagTypeSize (tagType); + + uint64 tagOffset = stream.Position (); + + TempBigEndian tag_order (stream, order_mark == byteOrderMM); + + ParseTag (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset, + 0); + + stream.SetReadPosition (tagOffset + tagSize); + + } + + } + + } + + section_offset += 8 + section_count; + + if (section_offset & 1) + { + section_offset++; + } + + } + + } + +/*****************************************************************************/ + +void dng_info::Parse (dng_host &host, + dng_stream &stream) + { + + fTIFFBlockOffset = stream.Position (); + + fTIFFBlockOriginalOffset = stream.PositionInOriginalFile (); + + // Check byte order indicator. + + uint16 byteOrder = stream.Get_uint16 (); + + if (byteOrder == byteOrderII) + { + + fBigEndian = false; + + #if qDNGValidate + + if (gVerbose) + { + printf ("\nUses little-endian byte order\n"); + } + + #endif + + stream.SetLittleEndian (); + + } + + else if (byteOrder == byteOrderMM) + { + + fBigEndian = true; + + #if qDNGValidate + + if (gVerbose) + { + printf ("\nUses big-endian byte order\n"); + } + + #endif + + stream.SetBigEndian (); + + } + + else + { + + #if qDNGValidate + + ReportError ("Unknown byte order"); + + #endif + + ThrowBadFormat (); + + } + + // Check "magic number" indicator. + + fMagic = stream.Get_uint16 (); + + #if qDNGValidate + + if (gVerbose) + { + printf ("Magic number = %u\n\n", (unsigned) fMagic); + } + + #endif + + ValidateMagic (); + + // Parse IFD 0. + + uint64 next_offset = stream.Get_uint32 (); + + fExif.Reset (host.Make_dng_exif ()); + + fShared.Reset (host.Make_dng_shared ()); + + fIFD [0].Reset (host.Make_dng_ifd ()); + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + fIFD [0].Get (), + fTIFFBlockOffset + next_offset, + fTIFFBlockOffset, + 0); + + next_offset = fIFD [0]->fNextIFD; + + fIFDCount = 1; + + // Parse chained IFDs. + + while (next_offset) + { + + if (next_offset >= stream.Length ()) + { + + #if qDNGValidate + + { + + ReportWarning ("Chained IFD offset past end of stream"); + + } + + #endif + + break; + + } + + // Some TIFF file writers forget about the next IFD offset, so + // validate the IFD at that offset before parsing it. + + if (!ValidateIFD (stream, + fTIFFBlockOffset + next_offset, + fTIFFBlockOffset)) + { + + #if qDNGValidate + + { + + ReportWarning ("Chained IFD is not valid"); + + } + + #endif + + break; + + } + + if (fChainedIFDCount == kMaxChainedIFDs) + { + + #if qDNGValidate + + { + + ReportWarning ("Chained IFD count exceeds DNG SDK parsing limit"); + + } + + #endif + + break; + + } + + fChainedIFD [fChainedIFDCount].Reset (host.Make_dng_ifd ()); + + ParseIFD (host, + stream, + NULL, + NULL, + fChainedIFD [fChainedIFDCount].Get (), + fTIFFBlockOffset + next_offset, + fTIFFBlockOffset, + tcFirstChainedIFD + fChainedIFDCount); + + next_offset = fChainedIFD [fChainedIFDCount]->fNextIFD; + + fChainedIFDCount++; + + } + + // Parse SubIFDs. + + uint32 searchedIFDs = 0; + + bool tooManySubIFDs = false; + + while (searchedIFDs < fIFDCount && !tooManySubIFDs) + { + + uint32 searchLimit = fIFDCount; + + for (uint32 searchIndex = searchedIFDs; + searchIndex < searchLimit && !tooManySubIFDs; + searchIndex++) + { + + for (uint32 subIndex = 0; + subIndex < fIFD [searchIndex]->fSubIFDsCount; + subIndex++) + { + + if (fIFDCount == kMaxSubIFDs + 1) + { + + tooManySubIFDs = true; + + break; + + } + + stream.SetReadPosition (fIFD [searchIndex]->fSubIFDsOffset + + subIndex * 4); + + uint32 sub_ifd_offset = stream.Get_uint32 (); + + fIFD [fIFDCount].Reset (host.Make_dng_ifd ()); + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + fIFD [fIFDCount].Get (), + fTIFFBlockOffset + sub_ifd_offset, + fTIFFBlockOffset, + tcFirstSubIFD + fIFDCount - 1); + + fIFDCount++; + + } + + searchedIFDs = searchLimit; + + } + + } + + #if qDNGValidate + + { + + if (tooManySubIFDs) + { + + ReportWarning ("SubIFD count exceeds DNG SDK parsing limit"); + + } + + } + + #endif + + // Parse EXIF IFD. + + if (fShared->fExifIFD) + { + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + fTIFFBlockOffset + fShared->fExifIFD, + fTIFFBlockOffset, + tcExifIFD); + + } + + // Parse GPS IFD. + + if (fShared->fGPSInfo) + { + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + fTIFFBlockOffset + fShared->fGPSInfo, + fTIFFBlockOffset, + tcGPSInfo); + + } + + // Parse Interoperability IFD. + + if (fShared->fInteroperabilityIFD) + { + + // Some Kodak KDC files have bogus Interoperability IFDs, so + // validate the IFD before trying to parse it. + + if (ValidateIFD (stream, + fTIFFBlockOffset + fShared->fInteroperabilityIFD, + fTIFFBlockOffset)) + { + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + fTIFFBlockOffset + fShared->fInteroperabilityIFD, + fTIFFBlockOffset, + tcInteroperabilityIFD); + + } + + #if qDNGValidate + + else + { + + ReportWarning ("The Interoperability IFD is not a valid IFD"); + + } + + #endif + + } + + // Parse Kodak DCR Private IFD. + + if (fShared->fKodakDCRPrivateIFD) + { + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + fTIFFBlockOffset + fShared->fKodakDCRPrivateIFD, + fTIFFBlockOffset, + tcKodakDCRPrivateIFD); + + } + + // Parse Kodak KDC Private IFD. + + if (fShared->fKodakKDCPrivateIFD) + { + + ParseIFD (host, + stream, + fExif.Get (), + fShared.Get (), + NULL, + fTIFFBlockOffset + fShared->fKodakKDCPrivateIFD, + fTIFFBlockOffset, + tcKodakKDCPrivateIFD); + + } + + // Parse MakerNote tag. + + if (fShared->fMakerNoteCount) + { + + ParseMakerNote (host, + stream, + (uint32) (fTIFFBlockOffset + fShared->fMakerNoteCount), + fShared->fMakerNoteOffset, + fTIFFBlockOffset, + 0, + stream.Length ()); + + } + + // Parse DNGPrivateData tag. + + if (fShared->fDNGPrivateDataCount && + fShared->fDNGVersion) + { + + ParseDNGPrivateData (host, stream); + + } + + #if qDNGValidate + + // If we are running dng_validate on stand-alone camera profile file, + // complete the validation of the profile. + + if (fMagic == magicExtendedProfile) + { + + dng_camera_profile_info &profileInfo = fShared->fCameraProfile; + + dng_camera_profile profile; + + profile.Parse (stream, profileInfo); + + if (profileInfo.fColorPlanes < 3 || !profile.IsValid (profileInfo.fColorPlanes)) + { + + ReportError ("Invalid camera profile file"); + + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_info::PostParse (dng_host &host) + { + + uint32 index; + + fExif->PostParse (host, *fShared.Get ()); + + fShared->PostParse (host, *fExif.Get ()); + + for (index = 0; index < fIFDCount; index++) + { + + fIFD [index]->PostParse (); + + } + + for (index = 0; index < fChainedIFDCount; index++) + { + + fChainedIFD [index]->PostParse (); + + } + + if (fShared->fDNGVersion != 0) + { + + // Find main IFD. + + fMainIndex = -1; + + for (index = 0; index < fIFDCount; index++) + { + + if (fIFD [index]->fUsesNewSubFileType && + fIFD [index]->fNewSubFileType == sfMainImage) + { + + if (fMainIndex == -1) + { + + fMainIndex = index; + + } + + #if qDNGValidate + + else + { + + ReportError ("Multiple IFDs marked as main image"); + + } + + #endif + + } + + else if (fIFD [index]->fNewSubFileType == sfPreviewImage || + fIFD [index]->fNewSubFileType == sfAltPreviewImage) + { + + // Fill in default color space for DNG previews if not included. + + if (fIFD [index]->fPreviewInfo.fColorSpace == previewColorSpace_MaxEnum) + { + + if (fIFD [index]->fSamplesPerPixel == 1) + { + + fIFD [index]->fPreviewInfo.fColorSpace = previewColorSpace_GrayGamma22; + + } + + else + { + + fIFD [index]->fPreviewInfo.fColorSpace = previewColorSpace_sRGB; + + } + + } + + } + + } + + // Deal with lossless JPEG bug in early DNG versions. + + if (fShared->fDNGVersion < dngVersion_1_1_0_0) + { + + if (fMainIndex != -1) + { + + fIFD [fMainIndex]->fLosslessJPEGBug16 = true; + + } + + } + + // Find mask index. + + for (index = 0; index < fIFDCount; index++) + { + + if (fIFD [index]->fNewSubFileType == sfTransparencyMask) + { + + if (fMaskIndex == -1) + { + + fMaskIndex = index; + + } + + #if qDNGValidate + + else + { + + ReportError ("Multiple IFDs marked as transparency mask image"); + + } + + #endif + + } + + } + + // Warn about Chained IFDs. + + #if qDNGValidate + + if (fChainedIFDCount > 0) + { + + ReportWarning ("This file has Chained IFDs, which will be ignored by DNG readers"); + + } + + #endif + + } + + } + +/*****************************************************************************/ + +bool dng_info::IsValidDNG () + { + + // Check shared info. + + if (!fShared->IsValidDNG ()) + { + + return false; + + } + + // Check TIFF magic number. + + if (fMagic != 42) + { + + #if qDNGValidate + + ReportError ("Invalid TIFF magic number"); + + #endif + + return false; + + } + + // Make sure we have a main image IFD. + + if (fMainIndex == -1) + { + + #if qDNGValidate + + ReportError ("Unable to find main image IFD"); + + #endif + + return false; + + } + + // Make sure is each IFD is valid. + + for (uint32 index = 0; index < fIFDCount; index++) + { + + uint32 parentCode = (index == 0 ? 0 : tcFirstSubIFD + index - 1); + + if (!fIFD [index]->IsValidDNG (*fShared.Get (), + parentCode)) + { + + // Only errors in the main and transparency mask IFDs are fatal to parsing. + + if (index == (uint32) fMainIndex || + index == (uint32) fMaskIndex) + { + + return false; + + } + + } + + } + + return true; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_info.h b/source/lib/dng_sdk/dng_info.h new file mode 100644 index 0000000..3ccefd1 --- /dev/null +++ b/source/lib/dng_sdk/dng_info.h @@ -0,0 +1,163 @@ +/*****************************************************************************/ +// Copyright 2006-2011 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_info.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Class for holding top-level information about a DNG image. + */ + +/*****************************************************************************/ + +#ifndef __dng_info__ +#define __dng_info__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_ifd.h" +#include "dng_exif.h" +#include "dng_shared.h" +#include "dng_errors.h" +#include "dng_sdk_limits.h" +#include "dng_auto_ptr.h" + +/*****************************************************************************/ + +/// \brief Top-level structure of DNG file with access to metadata. +/// +/// See \ref spec_dng "DNG 1.1.0 specification" for information on member fields of this class. + +class dng_info + { + + public: + + uint64 fTIFFBlockOffset; + + uint64 fTIFFBlockOriginalOffset; + + bool fBigEndian; + + uint32 fMagic; + + AutoPtr fExif; + + AutoPtr fShared; + + int32 fMainIndex; + + int32 fMaskIndex; + + uint32 fIFDCount; + + AutoPtr fIFD [kMaxSubIFDs + 1]; + + uint32 fChainedIFDCount; + + AutoPtr fChainedIFD [kMaxChainedIFDs]; + + protected: + + uint32 fMakerNoteNextIFD; + + public: + + dng_info (); + + virtual ~dng_info (); + + /// Read dng_info from a dng_stream + /// \param host DNG host used for progress updating, abort testing, buffer allocation, etc. + /// \param stream Stream to read DNG data from. + + virtual void Parse (dng_host &host, + dng_stream &stream); + + /// Must be called immediately after a successful Parse operation. + + virtual void PostParse (dng_host &host); + + /// Test validity of DNG data. + /// \retval true if stream provided a valid DNG. + + virtual bool IsValidDNG (); + + protected: + + virtual void ValidateMagic (); + + virtual void ParseTag (dng_host &host, + dng_stream &stream, + dng_exif *exif, + dng_shared *shared, + dng_ifd *ifd, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset, + int64 offsetDelta); + + virtual bool ValidateIFD (dng_stream &stream, + uint64 ifdOffset, + int64 offsetDelta); + + virtual void ParseIFD (dng_host &host, + dng_stream &stream, + dng_exif *exif, + dng_shared *shared, + dng_ifd *ifd, + uint64 ifdOffset, + int64 offsetDelta, + uint32 parentCode); + + virtual bool ParseMakerNoteIFD (dng_host &host, + dng_stream &stream, + uint64 ifdSize, + uint64 ifdOffset, + int64 offsetDelta, + uint64 minOffset, + uint64 maxOffset, + uint32 parentCode); + + virtual void ParseMakerNote (dng_host &host, + dng_stream &stream, + uint32 makerNoteCount, + uint64 makerNoteOffset, + int64 offsetDelta, + uint64 minOffset, + uint64 maxOffset); + + virtual void ParseSonyPrivateData (dng_host &host, + dng_stream &stream, + uint64 count, + uint64 oldOffset, + uint64 newOffset); + + virtual void ParseDNGPrivateData (dng_host &host, + dng_stream &stream); + + private: + + // Hidden copy constructor and assignment operator. + + dng_info (const dng_info &info); + + dng_info & operator= (const dng_info &info); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_iptc.cpp b/source/lib/dng_sdk/dng_iptc.cpp new file mode 100644 index 0000000..98c263d --- /dev/null +++ b/source/lib/dng_sdk/dng_iptc.cpp @@ -0,0 +1,987 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_iptc.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_iptc.h" + +#include "dng_assertions.h" +#include "dng_auto_ptr.h" +#include "dng_memory_stream.h" +#include "dng_stream.h" +#include "dng_utils.h" + +#include "stdc_includes.h" + +/*****************************************************************************/ + +dng_iptc::dng_iptc () + + : fTitle () + + , fUrgency (-1) + + , fCategory () + + , fSupplementalCategories () + + , fKeywords () + + , fInstructions () + + , fDateTimeCreated () + + , fDigitalCreationDateTime () + + , fAuthors () + , fAuthorsPosition () + + , fCity () + , fState () + , fCountry () + , fCountryCode () + + , fLocation () + + , fTransmissionReference () + + , fHeadline () + + , fCredit () + + , fSource () + + , fCopyrightNotice () + + , fDescription () + , fDescriptionWriter () + + { + + } + +/*****************************************************************************/ + +dng_iptc::~dng_iptc () + { + + } + +/*****************************************************************************/ + +bool dng_iptc::IsEmpty () const + { + + if (fTitle.NotEmpty ()) + { + return false; + } + + if (fUrgency >= 0) + { + return false; + } + + if (fCategory.NotEmpty ()) + { + return false; + } + + if (fSupplementalCategories.Count () > 0) + { + return false; + } + + if (fKeywords.Count () > 0) + { + return false; + } + + if (fInstructions.NotEmpty ()) + { + return false; + } + + if (fDateTimeCreated.IsValid ()) + { + return false; + } + + if (fDigitalCreationDateTime.IsValid ()) + { + return false; + } + + if (fAuthors.Count () != 0 || + fAuthorsPosition.NotEmpty ()) + { + return false; + } + + if (fCity .NotEmpty () || + fState .NotEmpty () || + fCountry.NotEmpty ()) + { + return false; + } + + if (fCountryCode.NotEmpty ()) + { + return false; + } + + if (fLocation.NotEmpty ()) + { + return false; + } + + if (fTransmissionReference.NotEmpty ()) + { + return false; + } + + if (fHeadline.NotEmpty ()) + { + return false; + } + + if (fCredit.NotEmpty ()) + { + return false; + } + + if (fSource.NotEmpty ()) + { + return false; + } + + if (fCopyrightNotice.NotEmpty ()) + { + return false; + } + + if (fDescription .NotEmpty () || + fDescriptionWriter.NotEmpty ()) + { + return false; + } + + return true; + + } + +/*****************************************************************************/ + +void dng_iptc::ParseString (dng_stream &stream, + dng_string &s, + CharSet charSet) + { + + uint32 length = stream.Get_uint16 (); + + dng_memory_data buffer (length + 1); + + char *c = buffer.Buffer_char (); + + stream.Get (c, length); + + c [length] = 0; + + switch (charSet) + { + + case kCharSetUTF8: + { + s.Set_UTF8 (c); + break; + } + + default: + { + s.Set_SystemEncoding (c); + } + + } + + s.SetLineEndingsToNewLines (); + + s.StripLowASCII (); + + s.TrimTrailingBlanks (); + + } + +/*****************************************************************************/ + +void dng_iptc::Parse (const void *blockData, + uint32 blockSize, + uint64 offsetInOriginalFile) + { + + dng_stream stream (blockData, + blockSize, + offsetInOriginalFile); + + stream.SetBigEndian (); + + // Make a first pass though the data, trying to figure out the + // character set. + + CharSet charSet = kCharSetUnknown; + + bool isValidUTF8 = true; + + bool hasEncodingMarker = false; + + uint64 firstOffset = stream.Position (); + + uint64 nextOffset = firstOffset; + + while (nextOffset + 5 < stream.Length ()) + { + + stream.SetReadPosition (nextOffset); + + uint8 firstByte = stream.Get_uint8 (); + + if (firstByte != 0x1C) break; + + uint8 record = stream.Get_uint8 (); + uint8 dataSet = stream.Get_uint8 (); + uint32 dataSize = stream.Get_uint16 (); + + nextOffset = stream.Position () + dataSize; + + if (record == 1) + { + + switch (dataSet) + { + + case 90: + { + + hasEncodingMarker = true; + + if (dataSize == 3) + { + + uint32 byte1 = stream.Get_uint8 (); + uint32 byte2 = stream.Get_uint8 (); + uint32 byte3 = stream.Get_uint8 (); + + if (byte1 == 27 /* Escape */ && + byte2 == 0x25 && + byte3 == 0x47) + { + + charSet = kCharSetUTF8; + + } + + } + + break; + + } + + default: + break; + + } + + } + + else if (record == 2) + { + + dng_memory_data buffer (dataSize + 1); + + char *s = buffer.Buffer_char (); + + stream.Get (s, dataSize); + + s [dataSize] = 0; + + isValidUTF8 = isValidUTF8 && dng_string::IsUTF8 (s); + + } + + } + + // If we don't have an encoding marker, and the data is valid + // UTF-8, then assume that it is UTF-8 (rather than system encoding). + + if (!hasEncodingMarker && isValidUTF8) + { + + charSet = kCharSetUTF8; + + } + + // Make a second pass though the data, actually reading the data. + + nextOffset = firstOffset; + + while (nextOffset + 5 < stream.Length ()) + { + + stream.SetReadPosition (nextOffset); + + uint8 firstByte = stream.Get_uint8 (); + + if (firstByte != 0x1C) break; + + uint8 record = stream.Get_uint8 (); + uint8 dataSet = stream.Get_uint8 (); + uint32 dataSize = stream.Get_uint16 (); + + nextOffset = stream.Position () + dataSize; + + if (record == 2) + { + + stream.SetReadPosition (stream.Position () - 2); + + switch ((DataSet) dataSet) + { + + case kObjectNameSet: + { + ParseString (stream, fTitle, charSet); + break; + } + + case kUrgencySet: + { + + int32 size = stream.Get_uint16 (); + + if (size == 1) + { + + char c = stream.Get_int8 (); + + if (c >= '0' && c <= '9') + { + fUrgency = c - '0'; + } + + } + + break; + + } + + case kCategorySet: + { + ParseString (stream, fCategory, charSet); + break; + } + + case kSupplementalCategoriesSet: + { + + dng_string category; + + ParseString (stream, category, charSet); + + if (category.NotEmpty ()) + { + fSupplementalCategories.Append (category); + } + + break; + + } + + case kKeywordsSet: + { + + dng_string keyword; + + ParseString (stream, keyword, charSet); + + if (keyword.NotEmpty ()) + { + fKeywords.Append (keyword); + } + + break; + + } + + case kSpecialInstructionsSet: + { + ParseString (stream, fInstructions, charSet); + break; + } + + case kDateCreatedSet: + { + + uint32 length = stream.Get_uint16 (); + + if (length == 8) + { + + char date [9]; + + stream.Get (date, 8); + + date [8] = 0; + + fDateTimeCreated.Decode_IPTC_Date (date); + + } + + break; + + } + + case kTimeCreatedSet: + { + + uint32 length = stream.Get_uint16 (); + + if (length >= 4 && length <= 11) + { + + char time [12]; + + stream.Get (time, length); + + time [length] = 0; + + fDateTimeCreated.Decode_IPTC_Time (time); + + } + + break; + + } + + case kDigitalCreationDateSet: + { + + uint32 length = stream.Get_uint16 (); + + if (length == 8) + { + + char date [9]; + + stream.Get (date, 8); + + date [8] = 0; + + fDigitalCreationDateTime.Decode_IPTC_Date (date); + + } + + break; + + } + + case kDigitalCreationTimeSet: + { + + uint32 length = stream.Get_uint16 (); + + if (length >= 4 && length <= 11) + { + + char time [12]; + + stream.Get (time, length); + + time [length] = 0; + + fDigitalCreationDateTime.Decode_IPTC_Time (time); + + } + + break; + + } + + case kBylineSet: + { + + dng_string author; + + ParseString (stream, author, charSet); + + if (author.NotEmpty ()) + { + fAuthors.Append (author); + } + + break; + + } + + case kBylineTitleSet: + { + ParseString (stream, fAuthorsPosition, charSet); + break; + } + + case kCitySet: + { + ParseString (stream, fCity, charSet); + break; + } + + case kProvinceStateSet: + { + ParseString (stream, fState, charSet); + break; + } + + case kCountryNameSet: + { + ParseString (stream, fCountry, charSet); + break; + } + + case kCountryCodeSet: + { + ParseString (stream, fCountryCode, charSet); + break; + } + + case kSublocationSet: + { + ParseString (stream, fLocation, charSet); + break; + } + + case kOriginalTransmissionReferenceSet: + { + ParseString (stream, fTransmissionReference, charSet); + break; + } + + case kHeadlineSet: + { + ParseString (stream, fHeadline, charSet); + break; + } + + case kCreditSet: + { + ParseString (stream, fCredit, charSet); + break; + } + + case kSourceSet: + { + ParseString (stream, fSource, charSet); + break; + } + + case kCopyrightNoticeSet: + { + ParseString (stream, fCopyrightNotice, charSet); + break; + } + + case kCaptionSet: + { + ParseString (stream, fDescription, charSet); + break; + } + + case kCaptionWriterSet: + { + ParseString (stream, fDescriptionWriter, charSet); + break; + } + + // All other IPTC records are not part of the IPTC core + // and/or are not kept in sync with XMP tags, so we ignore + // them. + + default: + break; + + } + + } + + } + + } + +/*****************************************************************************/ + +void dng_iptc::SpoolString (dng_stream &stream, + const dng_string &s, + uint8 dataSet, + uint32 maxChars, + CharSet charSet) + { + + if (s.IsEmpty ()) + { + return; + } + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (dataSet); + + dng_string ss (s); + + ss.SetLineEndingsToReturns (); + + if (charSet == kCharSetUTF8) + { + + // UTF-8 encoding. + + if (ss.Length () > maxChars) + { + ss.Truncate (maxChars); + } + + uint32 len = ss.Length (); + + stream.Put_uint16 ((uint16) len); + + stream.Put (ss.Get (), len); + + } + + else + { + + // System character set encoding. + + dng_memory_data buffer; + + uint32 len = ss.Get_SystemEncoding (buffer); + + if (len > maxChars) + { + + uint32 lower = 0; + uint32 upper = ss.Length () - 1; + + while (upper > lower) + { + + uint32 middle = (upper + lower + 1) >> 1; + + dng_string sss (ss); + + sss.Truncate (middle); + + len = sss.Get_SystemEncoding (buffer); + + if (len <= maxChars) + { + + lower = middle; + + } + + else + { + + upper = middle - 1; + + } + + } + + ss.Truncate (lower); + + len = ss.Get_SystemEncoding (buffer); + + } + + stream.Put_uint16 ((uint16) len); + + stream.Put (buffer.Buffer_char (), len); + + } + + } +/*****************************************************************************/ + +dng_memory_block * dng_iptc::Spool (dng_memory_allocator &allocator, + bool padForTIFF) + { + + uint32 j; + + char s [64]; + + dng_memory_stream stream (allocator, NULL, 2048); + + stream.SetBigEndian (); + + // Medata working group - now we just always write UTF-8. + + CharSet charSet = kCharSetUTF8; + + // UTF-8 encoding marker. + + if (charSet == kCharSetUTF8) + { + + stream.Put_uint16 (0x1C01); + stream.Put_uint8 (90); + stream.Put_uint16 (3); + stream.Put_uint8 (27); + stream.Put_uint8 (0x25); + stream.Put_uint8 (0x47); + + } + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (kRecordVersionSet); + stream.Put_uint16 (2); + stream.Put_uint16 (4); + + SpoolString (stream, + fTitle, + kObjectNameSet, + 64, + charSet); + + if (fUrgency >= 0) + { + + sprintf (s, "%1u", (unsigned) fUrgency); + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (kUrgencySet); + + stream.Put_uint16 (1); + + stream.Put (s, 1); + + } + + SpoolString (stream, + fCategory, + kCategorySet, + 3, + charSet); + + for (j = 0; j < fSupplementalCategories.Count (); j++) + { + + SpoolString (stream, + fSupplementalCategories [j], + kSupplementalCategoriesSet, + 32, + charSet); + + } + + for (j = 0; j < fKeywords.Count (); j++) + { + + SpoolString (stream, + fKeywords [j], + kKeywordsSet, + 64, + charSet); + + } + + SpoolString (stream, + fInstructions, + kSpecialInstructionsSet, + 255, + charSet); + + if (fDateTimeCreated.IsValid ()) + { + + dng_string dateString = fDateTimeCreated.Encode_IPTC_Date (); + + if (dateString.NotEmpty ()) + { + + DNG_ASSERT (dateString.Length () == 8, "Wrong length IPTC date"); + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (kDateCreatedSet); + + stream.Put_uint16 (8); + + stream.Put (dateString.Get (), 8); + + } + + dng_string timeString = fDateTimeCreated.Encode_IPTC_Time (); + + if (timeString.NotEmpty ()) + { + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (kTimeCreatedSet); + + stream.Put_uint16 ((uint16)timeString.Length ()); + + stream.Put (timeString.Get (), timeString.Length ()); + + } + + } + + if (fDigitalCreationDateTime.IsValid ()) + { + + dng_string dateString = fDigitalCreationDateTime.Encode_IPTC_Date (); + + if (dateString.NotEmpty ()) + { + + DNG_ASSERT (dateString.Length () == 8, "Wrong length IPTC date"); + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (kDigitalCreationDateSet); + + stream.Put_uint16 (8); + + stream.Put (dateString.Get (), 8); + + } + + dng_string timeString = fDigitalCreationDateTime.Encode_IPTC_Time (); + + if (timeString.NotEmpty ()) + { + + stream.Put_uint16 (0x1C02); + stream.Put_uint8 (kDigitalCreationTimeSet); + + stream.Put_uint16 ((uint16)timeString.Length ()); + + stream.Put (timeString.Get (), timeString.Length ()); + + } + + } + + for (j = 0; j < fAuthors.Count (); j++) + { + + SpoolString (stream, + fAuthors [j], + kBylineSet, + 32, + charSet); + + } + + SpoolString (stream, + fAuthorsPosition, + kBylineTitleSet, + 32, + charSet); + + SpoolString (stream, + fCity, + kCitySet, + 32, + charSet); + + SpoolString (stream, + fLocation, + kSublocationSet, + 32, + charSet); + + SpoolString (stream, + fState, + kProvinceStateSet, + 32, + charSet); + + SpoolString (stream, + fCountryCode, + kCountryCodeSet, + 3, + charSet); + + SpoolString (stream, + fCountry, + kCountryNameSet, + 64, + charSet); + + SpoolString (stream, + fTransmissionReference, + kOriginalTransmissionReferenceSet, + 32, + charSet); + + SpoolString (stream, + fHeadline, + kHeadlineSet, + 255, + charSet); + + SpoolString (stream, + fCredit, + kCreditSet, + 32, + charSet); + + SpoolString (stream, + fSource, + kSourceSet, + 32, + charSet); + + SpoolString (stream, + fCopyrightNotice, + kCopyrightNoticeSet, + 128, + charSet); + + SpoolString (stream, + fDescription, + kCaptionSet, + 2000, + charSet); + + SpoolString (stream, + fDescriptionWriter, + kCaptionWriterSet, + 32, + charSet); + + if (padForTIFF) + { + + while (stream.Length () & 3) + { + stream.Put_uint8 (0); + } + + } + + stream.Flush (); + + return stream.AsMemoryBlock (allocator); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_iptc.h b/source/lib/dng_sdk/dng_iptc.h new file mode 100644 index 0000000..82ebaeb --- /dev/null +++ b/source/lib/dng_sdk/dng_iptc.h @@ -0,0 +1,172 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_iptc.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Support for IPTC metadata within DNG files. + */ + +/*****************************************************************************/ + +#ifndef __dng_iptc__ +#define __dng_iptc__ + +/*****************************************************************************/ + +#include "dng_date_time.h" +#include "dng_string.h" +#include "dng_string_list.h" + +/*****************************************************************************/ + +/// \brief Class for reading and holding IPTC metadata associated with a DNG file. +/// +/// See the \ref spec_iptc "IPTC specification" +/// for information on member fields of this class. + +class dng_iptc + { + + public: + + dng_string fTitle; + + int32 fUrgency; + + dng_string fCategory; + + dng_string_list fSupplementalCategories; + + dng_string_list fKeywords; + + dng_string fInstructions; + + dng_date_time_info fDateTimeCreated; + + dng_date_time_info fDigitalCreationDateTime; + + dng_string_list fAuthors; + + dng_string fAuthorsPosition; + + dng_string fCity; + dng_string fState; + dng_string fCountry; + dng_string fCountryCode; + + dng_string fLocation; + + dng_string fTransmissionReference; + + dng_string fHeadline; + + dng_string fCredit; + + dng_string fSource; + + dng_string fCopyrightNotice; + + dng_string fDescription; + dng_string fDescriptionWriter; + + protected: + + enum DataSet + { + kRecordVersionSet = 0, + kObjectNameSet = 5, + kUrgencySet = 10, + kCategorySet = 15, + kSupplementalCategoriesSet = 20, + kKeywordsSet = 25, + kSpecialInstructionsSet = 40, + kDateCreatedSet = 55, + kTimeCreatedSet = 60, + kDigitalCreationDateSet = 62, + kDigitalCreationTimeSet = 63, + kBylineSet = 80, + kBylineTitleSet = 85, + kCitySet = 90, + kSublocationSet = 92, + kProvinceStateSet = 95, + kCountryCodeSet = 100, + kCountryNameSet = 101, + kOriginalTransmissionReferenceSet = 103, + kHeadlineSet = 105, + kCreditSet = 110, + kSourceSet = 115, + kCopyrightNoticeSet = 116, + kCaptionSet = 120, + kCaptionWriterSet = 122 + }; + + enum CharSet + { + kCharSetUnknown = 0, + kCharSetUTF8 = 1 + }; + + public: + + dng_iptc (); + + virtual ~dng_iptc (); + + /// Test if IPTC metadata exists. + /// \retval true if no IPTC metadata exists for this DNG. + + bool IsEmpty () const; + + /// Test if IPTC metadata exists. + /// \retval true if IPTC metadata exists for this DNG. + + bool NotEmpty () const + { + return !IsEmpty (); + } + + /// Parse a complete block of IPTC data. + /// \param blockData The block of IPTC data. + /// \param blockSize Size in bytes of data block. + /// \param offsetInOriginalFile Used to enable certain file patching operations such as updating date/time in place. + + void Parse (const void *blockData, + uint32 blockSize, + uint64 offsetInOriginalFile); + + /// Serialize IPTC data to a memory block. + /// \param allocator Memory allocator used to acquire memory block. + /// \param padForTIFF Forces length of block to be a multiple of four bytes in accordance with TIFF standard. + /// \retval Memory block + + dng_memory_block * Spool (dng_memory_allocator &allocator, + bool padForTIFF); + + protected: + + void ParseString (dng_stream &stream, + dng_string &s, + CharSet charSet); + + void SpoolString (dng_stream &stream, + const dng_string &s, + uint8 dataSet, + uint32 maxChars, + CharSet charSet); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_jpeg_image.cpp b/source/lib/dng_sdk/dng_jpeg_image.cpp new file mode 100644 index 0000000..362cc83 --- /dev/null +++ b/source/lib/dng_sdk/dng_jpeg_image.cpp @@ -0,0 +1,385 @@ +/*****************************************************************************/ +// Copyright 2011 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_jpeg_image.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_jpeg_image.h" + +#include "dng_abort_sniffer.h" +#include "dng_area_task.h" +#include "dng_assertions.h" +#include "dng_host.h" +#include "dng_ifd.h" +#include "dng_image.h" +#include "dng_image_writer.h" +#include "dng_memory_stream.h" +#include "dng_mutex.h" + +/*****************************************************************************/ + +dng_jpeg_image::dng_jpeg_image () + + : fImageSize () + , fTileSize () + , fUsesStrips (false) + , fJPEGTables () + , fJPEGData () + + { + + } + +/*****************************************************************************/ + +class dng_jpeg_image_encode_task : public dng_area_task + { + + private: + + dng_host &fHost; + + dng_image_writer &fWriter; + + const dng_image &fImage; + + dng_jpeg_image &fJPEGImage; + + uint32 fTileCount; + + const dng_ifd &fIFD; + + dng_mutex fMutex; + + uint32 fNextTileIndex; + + public: + + dng_jpeg_image_encode_task (dng_host &host, + dng_image_writer &writer, + const dng_image &image, + dng_jpeg_image &jpegImage, + uint32 tileCount, + const dng_ifd &ifd) + + : fHost (host) + , fWriter (writer) + , fImage (image) + , fJPEGImage (jpegImage) + , fTileCount (tileCount) + , fIFD (ifd) + , fMutex ("dng_jpeg_image_encode_task") + , fNextTileIndex (0) + + { + + fMinTaskArea = 16 * 16; + fUnitCell = dng_point (16, 16); + fMaxTileSize = dng_point (16, 16); + + } + + void Process (uint32 /* threadIndex */, + const dng_rect & /* tile */, + dng_abort_sniffer *sniffer) + { + + AutoPtr compressedBuffer; + AutoPtr uncompressedBuffer; + AutoPtr subTileBlockBuffer; + AutoPtr tempBuffer; + + uint32 uncompressedSize = fIFD.fTileLength * + fIFD.fTileWidth * + fIFD.fSamplesPerPixel; + + uncompressedBuffer.Reset (fHost.Allocate (uncompressedSize)); + + uint32 tilesAcross = fIFD.TilesAcross (); + + while (true) + { + + uint32 tileIndex; + + { + + dng_lock_mutex lock (&fMutex); + + if (fNextTileIndex == fTileCount) + { + return; + } + + tileIndex = fNextTileIndex++; + + } + + dng_abort_sniffer::SniffForAbort (sniffer); + + uint32 rowIndex = tileIndex / tilesAcross; + uint32 colIndex = tileIndex % tilesAcross; + + dng_rect tileArea = fIFD.TileArea (rowIndex, colIndex); + + dng_memory_stream stream (fHost.Allocator ()); + + fWriter.WriteTile (fHost, + fIFD, + stream, + fImage, + tileArea, + 1, + compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer, + tempBuffer); + + fJPEGImage.fJPEGData [tileIndex].Reset (stream.AsMemoryBlock (fHost.Allocator ())); + + } + + } + + private: + + // Hidden copy constructor and assignment operator. + + dng_jpeg_image_encode_task (const dng_jpeg_image_encode_task &); + + dng_jpeg_image_encode_task & operator= (const dng_jpeg_image_encode_task &); + + }; + +/*****************************************************************************/ + +void dng_jpeg_image::Encode (dng_host &host, + const dng_negative &negative, + dng_image_writer &writer, + const dng_image &image) + { + + #if qDNGValidate + dng_timer timer ("Encode JPEG Proxy time"); + #endif + + DNG_ASSERT (image.PixelType () == ttByte, "Cannot JPEG encode non-byte image"); + + fImageSize = image.Bounds ().Size (); + + dng_ifd ifd; + + ifd.fImageWidth = fImageSize.h; + ifd.fImageLength = fImageSize.v; + + ifd.fSamplesPerPixel = image.Planes (); + + ifd.fBitsPerSample [0] = 8; + ifd.fBitsPerSample [1] = 8; + ifd.fBitsPerSample [2] = 8; + ifd.fBitsPerSample [3] = 8; + + ifd.fPhotometricInterpretation = piLinearRaw; + + ifd.fCompression = ccLossyJPEG; + + ifd.FindTileSize (512 * 512 * ifd.fSamplesPerPixel); + + fTileSize.h = ifd.fTileWidth; + fTileSize.v = ifd.fTileLength; + + // Need a higher quality for raw proxies than non-raw proxies, + // since users often perform much greater color changes. Also, use + // we are targeting a "large" size proxy (larger than 5MP pixels), or this + // is a full size proxy, then use a higher quality. + + bool useHigherQuality = (uint64) ifd.fImageWidth * + (uint64) ifd.fImageLength > 5000000 || + image.Bounds ().Size () == negative.OriginalDefaultFinalSize (); + + if (negative.ColorimetricReference () == crSceneReferred) + { + ifd.fCompressionQuality = useHigherQuality ? 11 : 10; + } + else + { + ifd.fCompressionQuality = useHigherQuality ? 10 : 8; + } + + uint32 tilesAcross = ifd.TilesAcross (); + uint32 tilesDown = ifd.TilesDown (); + + uint32 tileCount = tilesAcross * tilesDown; + + fJPEGData.Reset (new dng_jpeg_image_tile_ptr [tileCount]); + + uint32 threadCount = Min_uint32 (tileCount, + host.PerformAreaTaskThreads ()); + + dng_jpeg_image_encode_task task (host, + writer, + image, + *this, + tileCount, + ifd); + + host.PerformAreaTask (task, + dng_rect (0, 0, 16, 16 * threadCount)); + + } + +/*****************************************************************************/ + +class dng_jpeg_image_find_digest_task : public dng_area_task + { + + private: + + const dng_jpeg_image &fJPEGImage; + + uint32 fTileCount; + + dng_fingerprint *fDigests; + + dng_mutex fMutex; + + uint32 fNextTileIndex; + + public: + + dng_jpeg_image_find_digest_task (const dng_jpeg_image &jpegImage, + uint32 tileCount, + dng_fingerprint *digests) + + : fJPEGImage (jpegImage) + , fTileCount (tileCount) + , fDigests (digests) + , fMutex ("dng_jpeg_image_find_digest_task") + , fNextTileIndex (0) + + { + + fMinTaskArea = 16 * 16; + fUnitCell = dng_point (16, 16); + fMaxTileSize = dng_point (16, 16); + + } + + void Process (uint32 /* threadIndex */, + const dng_rect & /* tile */, + dng_abort_sniffer *sniffer) + { + + while (true) + { + + uint32 tileIndex; + + { + + dng_lock_mutex lock (&fMutex); + + if (fNextTileIndex == fTileCount) + { + return; + } + + tileIndex = fNextTileIndex++; + + } + + dng_abort_sniffer::SniffForAbort (sniffer); + + dng_md5_printer printer; + + printer.Process (fJPEGImage.fJPEGData [tileIndex]->Buffer (), + fJPEGImage.fJPEGData [tileIndex]->LogicalSize ()); + + fDigests [tileIndex] = printer.Result (); + + } + + } + + private: + + // Hidden copy constructor and assignment operator. + + dng_jpeg_image_find_digest_task (const dng_jpeg_image_find_digest_task &); + + dng_jpeg_image_find_digest_task & operator= (const dng_jpeg_image_find_digest_task &); + + }; + +/*****************************************************************************/ + +dng_fingerprint dng_jpeg_image::FindDigest (dng_host &host) const + { + + uint32 tileCount = TileCount (); + + uint32 arrayCount = tileCount + (fJPEGTables.Get () ? 1 : 0); + + AutoArray digests (new dng_fingerprint [arrayCount]); + + // Compute digest of each compressed tile. + + { + + uint32 threadCount = Min_uint32 (tileCount, + host.PerformAreaTaskThreads ()); + + dng_jpeg_image_find_digest_task task (*this, + tileCount, + digests.Get ()); + + host.PerformAreaTask (task, + dng_rect (0, 0, 16, 16 * threadCount)); + + } + + // Compute digest of JPEG tables, if any. + + if (fJPEGTables.Get ()) + { + + dng_md5_printer printer; + + printer.Process (fJPEGTables->Buffer (), + fJPEGTables->LogicalSize ()); + + digests [tileCount] = printer.Result (); + + } + + // Combine digests into a single digest. + + { + + dng_md5_printer printer; + + for (uint32 k = 0; k < arrayCount; k++) + { + + printer.Process (digests [k].data, + dng_fingerprint::kDNGFingerprintSize); + + } + + return printer.Result (); + + } + + } + +/*****************************************************************************/ + diff --git a/source/lib/dng_sdk/dng_jpeg_image.h b/source/lib/dng_sdk/dng_jpeg_image.h new file mode 100644 index 0000000..01748bb --- /dev/null +++ b/source/lib/dng_sdk/dng_jpeg_image.h @@ -0,0 +1,92 @@ +/*****************************************************************************/ +// Copyright 2011 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_jpeg_image.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_jpeg_image__ +#define __dng_jpeg_image__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_memory.h" +#include "dng_point.h" + +/*****************************************************************************/ + +typedef AutoPtr dng_jpeg_image_tile_ptr; + +/*****************************************************************************/ + +class dng_jpeg_image + { + + public: + + dng_point fImageSize; + + dng_point fTileSize; + + bool fUsesStrips; + + AutoPtr fJPEGTables; + + AutoArray fJPEGData; + + public: + + dng_jpeg_image (); + + uint32 TilesAcross () const + { + if (fTileSize.h) + { + return (fImageSize.h + fTileSize.h - 1) / fTileSize.h; + } + else + { + return 0; + } + } + + uint32 TilesDown () const + { + if (fTileSize.v) + { + return (fImageSize.v + fTileSize.v - 1) / fTileSize.v; + } + else + { + return 0; + } + } + + uint32 TileCount () const + { + return TilesAcross () * TilesDown (); + } + + void Encode (dng_host &host, + const dng_negative &negative, + dng_image_writer &writer, + const dng_image &image); + + dng_fingerprint FindDigest (dng_host &host) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_lens_correction.cpp b/source/lib/dng_sdk/dng_lens_correction.cpp new file mode 100644 index 0000000..74f3c54 --- /dev/null +++ b/source/lib/dng_sdk/dng_lens_correction.cpp @@ -0,0 +1,2373 @@ +/*****************************************************************************/ +// Copyright 2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_lens_correction.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include +#include + +#include "dng_1d_table.h" +#include "dng_assertions.h" +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_filter_task.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_lens_correction.h" +#include "dng_negative.h" +#include "dng_sdk_limits.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +dng_warp_params::dng_warp_params () + + : fPlanes (1) + , fCenter (0.5, 0.5) + + { + + } + +/*****************************************************************************/ + +dng_warp_params::dng_warp_params (uint32 planes, + const dng_point_real64 ¢er) + + : fPlanes (planes) + , fCenter (center) + + { + + DNG_ASSERT (planes >= 1, "Too few planes." ); + DNG_ASSERT (planes <= kMaxColorPlanes, "Too many planes."); + + DNG_ASSERT (fCenter.h >= 0.0 && fCenter.h <= 1.0, + "Center (horizontal) out of range."); + + DNG_ASSERT (fCenter.v >= 0.0 && fCenter.v <= 1.0, + "Center (vertical) out of range."); + + } + +/*****************************************************************************/ + +dng_warp_params::~dng_warp_params () + { + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsNOPAll () const + { + + return IsRadNOPAll () && + IsTanNOPAll (); + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsNOP (uint32 plane) const + { + + return IsRadNOP (plane) && + IsTanNOP (plane); + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsRadNOPAll () const + { + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + if (!IsRadNOP (plane)) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsRadNOP (uint32 /* plane */) const + { + + return false; + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsTanNOPAll () const + { + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + if (!IsTanNOP (plane)) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsTanNOP (uint32 /* plane */) const + { + + return false; + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsValid () const + { + + if (fPlanes < 1 || fPlanes > kMaxColorPlanes) + { + + return false; + + } + + if (fCenter.h < 0.0 || + fCenter.h > 1.0 || + fCenter.v < 0.0 || + fCenter.v > 1.0) + { + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_warp_params::IsValidForNegative (const dng_negative &negative) const + { + + if (!IsValid ()) + { + return false; + } + + if ((fPlanes != 1) && + (fPlanes != negative.ColorChannels ())) + { + return false; + } + + return true; + + } + +/*****************************************************************************/ + +real64 dng_warp_params::EvaluateInverse (uint32 plane, + real64 y) const + { + + const uint32 kMaxIterations = 30; + const real64 kNearZero = 1.0e-10; + + real64 x0 = 0.0; + real64 y0 = Evaluate (plane, + x0); + + real64 x1 = 1.0; + real64 y1 = Evaluate (plane, + x1); + + for (uint32 iteration = 0; iteration < kMaxIterations; iteration++) + { + + if (Abs_real64 (y1 - y0) < kNearZero) + { + break; + } + + const real64 x2 = Pin_real64 (0.0, + x1 + (y - y1) * (x1 - x0) / (y1 - y0), + 1.0); + + const real64 y2 = Evaluate (plane, + x2); + + x0 = x1; + y0 = y1; + + x1 = x2; + y1 = y2; + + } + + return x1; + + } + +/*****************************************************************************/ + +dng_point_real64 dng_warp_params::EvaluateTangential2 (uint32 plane, + const dng_point_real64 &diff) const + { + + const real64 dvdv = diff.v * diff.v; + const real64 dhdh = diff.h * diff.h; + + const real64 rr = dvdv + dhdh; + + dng_point_real64 diffSqr (dvdv, + dhdh); + + return EvaluateTangential (plane, + rr, + diff, + diffSqr); + + } + +/*****************************************************************************/ + +dng_point_real64 dng_warp_params::EvaluateTangential3 (uint32 plane, + real64 r2, + const dng_point_real64 &diff) const + { + + dng_point_real64 diffSqr (diff.v * diff.v, + diff.h * diff.h); + + return EvaluateTangential (plane, + r2, + diff, + diffSqr); + + } + +/*****************************************************************************/ + +void dng_warp_params::Dump () const + { + + #if qDNGValidate + + printf ("Planes: %u\n", (unsigned) fPlanes); + + printf (" Optical center:\n" + " h = %.6lf\n" + " v = %.6lf\n", + fCenter.h, + fCenter.v); + + #endif + + } + +/*****************************************************************************/ + +dng_warp_params_rectilinear::dng_warp_params_rectilinear () + + : dng_warp_params () + + { + + for (uint32 plane = 0; plane < kMaxColorPlanes; plane++) + { + + fRadParams [plane] = dng_vector (4); + fTanParams [plane] = dng_vector (2); + + fRadParams [plane][0] = 1.0; + + } + + } + +/*****************************************************************************/ + +dng_warp_params_rectilinear::dng_warp_params_rectilinear (uint32 planes, + const dng_vector radParams [], + const dng_vector tanParams [], + const dng_point_real64 ¢er) + + : dng_warp_params (planes, + center) + + { + + for (uint32 i = 0; i < fPlanes; i++) + { + fRadParams [i] = radParams [i]; + fTanParams [i] = tanParams [i]; + } + + } + +/*****************************************************************************/ + +dng_warp_params_rectilinear::~dng_warp_params_rectilinear () + { + + } + +/*****************************************************************************/ + +bool dng_warp_params_rectilinear::IsRadNOP (uint32 plane) const + { + + DNG_ASSERT (plane < fPlanes, "plane out of range."); + + const dng_vector &r = fRadParams [plane]; + + return (r [0] == 1.0 && + r [1] == 0.0 && + r [2] == 0.0 && + r [3] == 0.0); + + } + +/*****************************************************************************/ + +bool dng_warp_params_rectilinear::IsTanNOP (uint32 plane) const + { + + DNG_ASSERT (plane < fPlanes, "plane out of range."); + + const dng_vector &t = fTanParams [plane]; + + return (t [0] == 0.0 && + t [1] == 0.0); + + } + +/*****************************************************************************/ + +bool dng_warp_params_rectilinear::IsValid () const + { + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + if (fRadParams [plane].Count () != 4) + { + return false; + } + + if (fTanParams [plane].Count () < 2) + { + return false; + } + + } + + return dng_warp_params::IsValid (); + + } + +/*****************************************************************************/ + +void dng_warp_params_rectilinear::PropagateToAllPlanes (uint32 totalPlanes) + { + + for (uint32 plane = fPlanes; plane < totalPlanes; plane++) + { + + fRadParams [plane] = fRadParams [0]; + fTanParams [plane] = fTanParams [0]; + + } + + } + +/*****************************************************************************/ + +real64 dng_warp_params_rectilinear::Evaluate (uint32 plane, + real64 x) const + { + + const dng_vector &K = fRadParams [plane]; // Coefficients. + + const real64 x2 = x * x; + + return x * (K [0] + x2 * (K [1] + x2 * (K [2] + x2 * K [3]))); + + } + +/*****************************************************************************/ + +real64 dng_warp_params_rectilinear::EvaluateRatio (uint32 plane, + real64 r2) const + { + + const dng_vector &K = fRadParams [plane]; // Coefficients. + + return K [0] + r2 * (K [1] + r2 * (K [2] + r2 * K [3])); + + } + +/*****************************************************************************/ + +dng_point_real64 dng_warp_params_rectilinear::EvaluateTangential (uint32 plane, + real64 r2, + const dng_point_real64 &diff, + const dng_point_real64 &diff2) const + { + + const real64 kt0 = fTanParams [plane][0]; + const real64 kt1 = fTanParams [plane][1]; + + const real64 dh = diff.h; + const real64 dv = diff.v; + + const real64 dhdh = diff2.h; + const real64 dvdv = diff2.v; + + return dng_point_real64 (kt0 * (r2 + 2.0 * dvdv) + (2.0 * kt1 * dh * dv), // v + kt1 * (r2 + 2.0 * dhdh) + (2.0 * kt0 * dh * dv)); // h + + } + +/*****************************************************************************/ + +real64 dng_warp_params_rectilinear::MaxSrcRadiusGap (real64 maxDstGap) const + { + + real64 maxSrcGap = 0.0; + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + const dng_vector &coefs = fRadParams [plane]; + + const real64 k3 = coefs [1]; + const real64 k5 = coefs [2]; + const real64 k7 = coefs [3]; + + // + // Let f (r) be the radius warp function. Consider the function + // + // gap (r) = f (r + maxDstGap) - f (r) + // + // We wish to maximize gap (r) over the domain [0, 1 - maxDstGap]. This will + // give us a reasonable upper bound on the src tile size, independent of + // dstBounds. + // + // As usual, we maximize gap (r) by examining its critical points, i.e., by + // considering the roots of its derivative which lie in the domain [0, 1 - + // maxDstGap]. gap' (r) is a 5th-order polynomial. One of its roots is + // -maxDstGap / 2, which is negative and hence lies outside the domain of + // interest. This leaves 4 other possible roots. We solve for these + // analytically below. + // + + real64 roots [4]; + uint32 numRoots = 0; + + if (k7 == 0.0) + { + + if (k5 == 0.0) + { + + // No roots in [0,1]. + + } + + else + { + + // k7 is zero, but k5 is non-zero. At most two real roots. + + const real64 discrim = 25.0 * (-6.0 * k3 * k5 - 5.0 * k5 * maxDstGap * maxDstGap); + + if (discrim >= 0.0) + { + + // Two real roots. + + const real64 scale = 0.1 * k5; + const real64 offset = -5.0 * maxDstGap * k5; + const real64 sDiscrim = sqrt (discrim); + + roots [numRoots++] = scale * (offset + sDiscrim); + roots [numRoots++] = scale * (offset - sDiscrim); + + } + + } + + } + + else + { + + // k7 is non-zero. Up to 4 real roots. + + const real64 d = maxDstGap; + const real64 d2 = d * d; + const real64 d4 = d2 * d2; + + const real64 discrim = 25.0 * k5 * k5 + - 63.0 * k3 * k7 + + 35.0 * d2 * k5 * k7 + + 49.0 * d4 * k7 * k7; + + if (discrim >= 0.0) + { + + const real64 sDiscrim = 4.0 * k7 * sqrt (discrim); + + const real64 offset = -20.0 * k5 * k7 - 35.0 * d2 * k7 * k7; + + const real64 discrim1 = offset - sDiscrim; + const real64 discrim2 = offset + sDiscrim; + + const real64 scale = sqrt (21.0) / (42.0 * k7); + + if (discrim1 >= 0.0) + { + + const real64 offset1 = -d * 0.5; + const real64 sDiscrim1 = scale * sqrt (discrim1); + + roots [numRoots++] = offset1 + sDiscrim1; + roots [numRoots++] = offset1 - sDiscrim1; + + } + + if (discrim2 >= 0.0) + { + + const real64 offset2 = -d * 0.5; + const real64 sDiscrim2 = scale * sqrt (discrim2); + + roots [numRoots++] = offset2 + sDiscrim2; + roots [numRoots++] = offset2 - sDiscrim2; + + } + + } + + } + + real64 planeMaxSrcGap = 0.0; + + // Examine the endpoints. + + { + + // Check left endpoint: f (maxDstGap) - f (0). Remember that f (0) == 0. + + const real64 gap1 = Evaluate (plane, maxDstGap); + + planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap1); + + // Check right endpoint: f (1) - f (1 - maxDstGap). + + const real64 gap2 = Evaluate (plane, 1.0) + - Evaluate (plane, 1.0 - maxDstGap); + + planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap2); + + } + + // Examine the roots we found, if any. + + for (uint32 i = 0; i < numRoots; i++) + { + + const real64 r = roots [i]; + + if (r > 0.0 && r < 1.0 - maxDstGap) + { + + const real64 gap = Evaluate (plane, r + maxDstGap) + - Evaluate (plane, r); + + planeMaxSrcGap = Max_real64 (planeMaxSrcGap, gap); + + } + + } + + maxSrcGap = Max_real64 (maxSrcGap, + planeMaxSrcGap); + + } + + return maxSrcGap; + + } + +/*****************************************************************************/ + +dng_point_real64 dng_warp_params_rectilinear::MaxSrcTanGap (dng_point_real64 minDst, + dng_point_real64 maxDst) const + { + + const real64 v [] = { minDst.v, maxDst.v, 0.0 }; + const real64 h [] = { minDst.h, maxDst.h, 0.0 }; + + dng_point_real64 maxGap; + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + real64 hMin = +FLT_MAX; + real64 hMax = -FLT_MAX; + + real64 vMin = +FLT_MAX; + real64 vMax = -FLT_MAX; + + for (uint32 i = 0; i < 3; i++) + { + + for (uint32 j = 0; j < 3; j++) + { + + dng_point_real64 dstDiff (v [i], + h [j]); + + dng_point_real64 srcDiff = EvaluateTangential2 (plane, + dstDiff); + + hMin = Min_real64 (hMin, srcDiff.h); + hMax = Max_real64 (hMax, srcDiff.h); + + vMin = Min_real64 (vMin, srcDiff.v); + vMax = Max_real64 (vMax, srcDiff.v); + + } + + } + + const real64 hGap = hMax - hMin; + const real64 vGap = vMax - vMin; + + maxGap.h = Max_real64 (maxGap.h, hGap); + maxGap.v = Max_real64 (maxGap.v, vGap); + + } + + return maxGap; + + } + +/*****************************************************************************/ + +void dng_warp_params_rectilinear::Dump () const + { + + #if qDNGValidate + + dng_warp_params::Dump (); + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + printf (" Plane %u:\n", (unsigned) plane); + + printf (" Radial params: %.6lf, %.6lf, %.6lf, %.6lf\n", + fRadParams [plane][0], + fRadParams [plane][1], + fRadParams [plane][2], + fRadParams [plane][3]); + + printf (" Tangential params: %.6lf, %.6lf\n", + fTanParams [plane][0], + fTanParams [plane][1]); + + } + + #endif + + } + +/*****************************************************************************/ + +dng_warp_params_fisheye::dng_warp_params_fisheye () + + : dng_warp_params () + + { + + for (uint32 plane = 0; plane < kMaxColorPlanes; plane++) + { + + fRadParams [plane] = dng_vector (4); + + } + + } + +/*****************************************************************************/ + +dng_warp_params_fisheye::dng_warp_params_fisheye (uint32 planes, + const dng_vector radParams [], + const dng_point_real64 ¢er) + + : dng_warp_params (planes, center) + + { + + for (uint32 i = 0; i < fPlanes; i++) + { + + fRadParams [i] = radParams [i]; + + } + + } + +/*****************************************************************************/ + +dng_warp_params_fisheye::~dng_warp_params_fisheye () + { + + } + +/*****************************************************************************/ + +bool dng_warp_params_fisheye::IsRadNOP (uint32 /* plane */) const + { + + return false; + + } + +/*****************************************************************************/ + +bool dng_warp_params_fisheye::IsTanNOP (uint32 /* plane */) const + { + + return true; + + } + +/*****************************************************************************/ + +bool dng_warp_params_fisheye::IsValid () const + { + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + if (fRadParams [plane].Count () != 4) + { + return false; + } + + } + + return dng_warp_params::IsValid (); + + } + +/*****************************************************************************/ + +void dng_warp_params_fisheye::PropagateToAllPlanes (uint32 totalPlanes) + { + + for (uint32 plane = fPlanes; plane < totalPlanes; plane++) + { + + fRadParams [plane] = fRadParams [0]; + + } + + } + +/*****************************************************************************/ + +real64 dng_warp_params_fisheye::Evaluate (uint32 plane, + real64 r) const + { + + const real64 t = atan (r); + + const dng_vector &K = fRadParams [plane]; + + const real64 t2 = t * t; + + return t * (K [0] + t2 * (K [1] + t2 * (K [2] + t2 * K [3]))); + + } + +/*****************************************************************************/ + +real64 dng_warp_params_fisheye::EvaluateRatio (uint32 plane, + real64 rSqr) const + { + + const real64 eps = 1.0e-12; + + if (rSqr < eps) + { + + // r is very close to zero. + + return 1.0; + + } + + const real64 r = sqrt (rSqr); + + return Evaluate (plane, r) / r; + + } + +/*****************************************************************************/ + +dng_point_real64 dng_warp_params_fisheye::EvaluateTangential (uint32 /* plane */, + real64 /* r2 */, + const dng_point_real64 & /* diff */, + const dng_point_real64 & /* diff2 */) const + { + + // This fisheye model does not support tangential warping. + + ThrowProgramError (); + + return dng_point_real64 (0.0, 0.0); + + } + +/*****************************************************************************/ + +real64 dng_warp_params_fisheye::MaxSrcRadiusGap (real64 maxDstGap) const + { + + // + // Let f (r) be the radius warp function. Consider the function + // + // gap (r) = f (r + maxDstGap) - f (r) + // + // We wish to maximize gap (r) over the domain [0, 1 - maxDstGap]. This will + // give us a reasonable upper bound on the src tile size, independent of + // dstBounds. + // + // Ideally, we'd like to maximize gap (r) by examining its critical points, + // i.e., by considering the roots of its derivative which lie in the domain [0, + // 1 - maxDstGap]. However, gap' (r) is too complex to find its roots + // analytically. + // + + real64 maxSrcGap = 0.0; + + DNG_REQUIRE (maxDstGap > 0.0, "maxDstGap must be positive."); + + const real64 kMaxValue = 1.0 - maxDstGap; + + const uint32 kSteps = 128; + + const real64 kNorm = kMaxValue / real64 (kSteps - 1); + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + for (uint32 i = 0; i < kSteps; i++) + { + + const real64 tt = i * kNorm; + + const real64 gap = Evaluate (plane, tt + maxDstGap) + - Evaluate (plane, tt); + + maxSrcGap = Max_real64 (maxSrcGap, + gap); + + } + + } + + return maxSrcGap; + + } + +/*****************************************************************************/ + +dng_point_real64 dng_warp_params_fisheye::MaxSrcTanGap (dng_point_real64 /* minDst */, + dng_point_real64 /* maxDst */) const + { + + // This fisheye model does not support tangential distortion. + + return dng_point_real64 (0.0, 0.0); + + } + +/*****************************************************************************/ + +void dng_warp_params_fisheye::Dump () const + { + + #if qDNGValidate + + dng_warp_params::Dump (); + + for (uint32 plane = 0; plane < fPlanes; plane++) + { + + printf (" Plane %u:\n", (unsigned) plane); + + printf (" Radial params: %.6lf, %.6lf, %.6lf, %.6lf\n", + fRadParams [plane][0], + fRadParams [plane][1], + fRadParams [plane][2], + fRadParams [plane][3]); + + } + + #endif + + } + +/*****************************************************************************/ + +class dng_filter_warp: public dng_filter_task + { + + protected: + + AutoPtr fParams; + + dng_point_real64 fCenter; + + dng_resample_weights_2d fWeights; + + real64 fNormRadius; + real64 fInvNormRadius; + + bool fIsRadNOP; + bool fIsTanNOP; + + const real64 fPixelScaleV; + const real64 fPixelScaleVInv; + + public: + + dng_filter_warp (const dng_image &srcImage, + dng_image &dstImage, + const dng_negative &negative, + AutoPtr ¶ms); + + virtual void Initialize (dng_host &host); + + virtual dng_rect SrcArea (const dng_rect &dstArea); + + virtual dng_point SrcTileSize (const dng_point &dstTileSize); + + virtual void ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer); + + virtual dng_point_real64 GetSrcPixelPosition (const dng_point_real64 &dst, + uint32 plane); + + }; + +/*****************************************************************************/ + +dng_filter_warp::dng_filter_warp (const dng_image &srcImage, + dng_image &dstImage, + const dng_negative &negative, + AutoPtr ¶ms) + + : dng_filter_task (srcImage, + dstImage) + + , fParams (params.Release ()) + + , fCenter () + + , fWeights () + + , fNormRadius (1.0) + , fInvNormRadius (1.0) + + , fIsRadNOP (false) + , fIsTanNOP (false) + + , fPixelScaleV (1.0 / negative.PixelAspectRatio ()) + , fPixelScaleVInv (1.0 / fPixelScaleV) + + { + + // Force float processing. + + fSrcPixelType = ttFloat; + fDstPixelType = ttFloat; + + fIsRadNOP = fParams->IsRadNOPAll (); + fIsTanNOP = fParams->IsTanNOPAll (); + + const uint32 negPlanes = negative.ColorChannels (); + + DNG_ASSERT (negPlanes >= 1, "Too few planes." ); + DNG_ASSERT (negPlanes <= kMaxColorPlanes, "Too many planes."); + + (void) negPlanes; + + // At least one set of params must do something interesting. + + if (fIsRadNOP && fIsTanNOP) + { + ThrowProgramError (); + } + + // Make sure the warp params are valid for this negative. + + if (!fParams->IsValidForNegative (negative)) + { + ThrowBadFormat (); + } + + // Compute center. + + const dng_rect bounds = srcImage.Bounds (); + + fCenter.h = Lerp_real64 ((real64) bounds.l, + (real64) bounds.r, + fParams->fCenter.h); + + fCenter.v = Lerp_real64 ((real64) bounds.t, + (real64) bounds.b, + fParams->fCenter.v); + + // Compute max pixel radius and derive various normalized radius values. Note + // that when computing the max pixel radius, we must take into account the pixel + // aspect ratio. + + { + + dng_rect squareBounds (bounds); + + squareBounds.b = squareBounds.t + + Round_int32 (fPixelScaleV * (real64) squareBounds.H ()); + + const dng_point_real64 squareCenter (Lerp_real64 ((real64) squareBounds.t, + (real64) squareBounds.b, + fParams->fCenter.v), + + Lerp_real64 ((real64) squareBounds.l, + (real64) squareBounds.r, + fParams->fCenter.h)); + + fNormRadius = MaxDistancePointToRect (squareCenter, + squareBounds); + + fInvNormRadius = 1.0 / fNormRadius; + + } + + // Propagate warp params to other planes. + + fParams->PropagateToAllPlanes (fDstPlanes); + + } + +/*****************************************************************************/ + +void dng_filter_warp::Initialize (dng_host &host) + { + + // Make resample weights. + + const dng_resample_function &kernel = dng_resample_bicubic::Get (); + + fWeights.Initialize (kernel, + host.Allocator ()); + + } + +/*****************************************************************************/ + +dng_rect dng_filter_warp::SrcArea (const dng_rect &dstArea) + { + + // Walk each pixel of the boundary of dstArea, map it to the uncorrected src + // pixel position, and return the rectangle that contains all such src pixels. + + int32 xMin = INT_MAX; + int32 xMax = INT_MIN; + int32 yMin = INT_MAX; + int32 yMax = INT_MIN; + + for (uint32 plane = 0; plane < fDstPlanes; plane++) + { + + // Top and bottom edges. + + for (int32 c = dstArea.l; c < dstArea.r; c++) + { + + // Top edge. + + { + + const dng_point_real64 dst (dstArea.t, c); + + const dng_point_real64 src = GetSrcPixelPosition (dst, plane); + + const int32 y = (int32) floor (src.v); + + yMin = Min_int32 (yMin, y); + + } + + // Bottom edge. + + { + + const dng_point_real64 dst (dstArea.b - 1, c); + + const dng_point_real64 src = GetSrcPixelPosition (dst, plane); + + const int32 y = (int32) ceil (src.v); + + yMax = Max_int32 (yMax, y); + + } + + } + + // Left and right edges. + + for (int32 r = dstArea.t; r < dstArea.b; r++) + { + + // Left edge. + + { + + const dng_point_real64 dst (r, dstArea.l); + + const dng_point_real64 src = GetSrcPixelPosition (dst, plane); + + const int32 x = (int32) floor (src.h); + + xMin = Min_int32 (xMin, x); + + } + + // Right edge. + + { + + const dng_point_real64 dst (r, dstArea.r - 1); + + const dng_point_real64 src = GetSrcPixelPosition (dst, plane); + + const int32 x = (int32) ceil (src.h); + + xMax = Max_int32 (xMax, x); + + } + + } + + } + + // Pad each side by filter radius. + + const int32 pad = (int32) fWeights.Radius (); + + xMin -= pad; + yMin -= pad; + xMax += pad; + yMax += pad; + + xMax += 1; + yMax += 1; + + const dng_rect srcArea (yMin, + xMin, + yMax, + xMax); + + return srcArea; + + } + +/*****************************************************************************/ + +dng_point dng_filter_warp::SrcTileSize (const dng_point &dstTileSize) + { + + // Obtain an upper bound on the source tile size. We'll do this by considering + // upper bounds on the radial and tangential warp components separately, then add + // them together. This is not a tight bound but is good enough because the + // tangential terms are usually quite small. + + // Get upper bound on src tile size from radial warp. + + DNG_REQUIRE (dstTileSize.v > 0, "Invalid tile height."); + DNG_REQUIRE (dstTileSize.h > 0, "Invalid tile width."); + + const real64 maxDstGap = fInvNormRadius * hypot ((real64) dstTileSize.h, + (real64) dstTileSize.v); + + dng_point srcTileSize; + + if (maxDstGap >= 1.0) + { + + // The proposed tile size is unusually large, i.e., its hypotenuse is larger + // than the maximum radius. Bite the bullet and just return a tile size big + // enough to process the whole image. + + srcTileSize = SrcArea (fDstImage.Bounds ()).Size (); + + } + + else + { + + // maxDstGap < 1.0. + + const real64 maxSrcGap = fParams->MaxSrcRadiusGap (maxDstGap); + + const int32 dim = (int32) ceil (maxSrcGap * fNormRadius); + + srcTileSize = dng_point (dim, dim); + + } + + srcTileSize.h += (int32) (fWeights.Width ()); + srcTileSize.v += (int32) (fWeights.Width ()); + + // Get upper bound on src tile size from tangential warp. + + const dng_rect_real64 bounds (fSrcImage.Bounds ()); + + const dng_point_real64 minDst ((bounds.t - fCenter.v) * fInvNormRadius, + (bounds.l - fCenter.h) * fInvNormRadius); + + const dng_point_real64 maxDst ((bounds.b - 1.0 - fCenter.v) * fInvNormRadius, + (bounds.r - 1.0 - fCenter.h) * fInvNormRadius); + + const dng_point_real64 srcTanGap = fParams->MaxSrcTanGap (minDst, + maxDst); + + // Add the two bounds together. + + srcTileSize.v += (int32) ceil (srcTanGap.v * fNormRadius); + srcTileSize.h += (int32) ceil (srcTanGap.h * fNormRadius); + + return srcTileSize; + + } + +/*****************************************************************************/ + +void dng_filter_warp::ProcessArea (uint32 /* threadIndex */, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) + { + + // Prepare resample constants. + + const int32 wCount = fWeights.Width (); + + const dng_point srcOffset (fWeights.Offset (), + fWeights.Offset ()); + + const real64 numSubsamples = (real64) kResampleSubsampleCount2D; + + // Prepare area and step constants. + + const dng_rect srcArea = srcBuffer.fArea; + const dng_rect dstArea = dstBuffer.fArea; + + const int32 srcRowStep = (int32) srcBuffer.RowStep (); + + const int32 hMin = srcArea.l; + const int32 hMax = srcArea.r - wCount - 1; + + const int32 vMin = srcArea.t; + const int32 vMax = srcArea.b - wCount - 1; + + // Warp each plane. + + for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++) + { + + real32 *dPtr = dstBuffer.DirtyPixel_real32 (dstArea.t, + dstArea.l, + plane); + + for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++) + { + + uint32 dstIndex = 0; + + for (int32 dstCol = dstArea.l; dstCol < dstArea.r; dstCol++, dstIndex++) + { + + // Get destination (corrected) pixel position. + + const dng_point_real64 dPos ((real64) dstRow, + (real64) dstCol); + + // Warp to source (uncorrected) pixel position. + + const dng_point_real64 sPos = GetSrcPixelPosition (dPos, + plane); + + // Decompose into integer and fractional parts. + + dng_point sInt ((int32) floor (sPos.v), + (int32) floor (sPos.h)); + + dng_point sFct ((int32) ((sPos.v - (real64) sInt.v) * numSubsamples), + (int32) ((sPos.h - (real64) sInt.h) * numSubsamples)); + + // Add resample offset. + + sInt = sInt + srcOffset; + + // Clip. + + if (sInt.h < hMin) + { + sInt.h = hMin; + sFct.h = 0; + } + + else if (sInt.h > hMax) + { + sInt.h = hMax; + sFct.h = 0; + } + + if (sInt.v < vMin) + { + sInt.v = vMin; + sFct.v = 0; + } + + else if (sInt.v > vMax) + { + sInt.v = vMax; + sFct.v = 0; + } + + // Perform 2D resample. + + const real32 *w = fWeights.Weights32 (sFct); + + const real32 *s = srcBuffer.ConstPixel_real32 (sInt.v, + sInt.h, + plane); + + real32 total = 0.0f; + + for (int32 i = 0; i < wCount; i++) + { + + for (int32 j = 0; j < wCount; j++) + { + + total += w [j] * s [j]; + + } + + w += wCount; + s += srcRowStep; + + } + + // Store final pixel value. + + dPtr [dstIndex] = Pin_real32 (total); + + } + + // Advance to next row. + + dPtr += dstBuffer.RowStep (); + + } + + } + + } + +/*****************************************************************************/ + +dng_point_real64 dng_filter_warp::GetSrcPixelPosition (const dng_point_real64 &dst, + uint32 plane) + { + + const dng_point_real64 diff = dst - fCenter; + + const dng_point_real64 diffNorm (diff.v * fInvNormRadius, + diff.h * fInvNormRadius); + + const dng_point_real64 diffNormScaled (diffNorm.v * fPixelScaleV, + diffNorm.h); + + const dng_point_real64 diffNormSqr (diffNormScaled.v * diffNormScaled.v, + diffNormScaled.h * diffNormScaled.h); + + const real64 rr = Min_real64 (diffNormSqr.v + diffNormSqr.h, + 1.0); + + dng_point_real64 dSrc; + + if (fIsTanNOP) + { + + // Radial only. + + const real64 ratio = fParams->EvaluateRatio (plane, + rr); + + dSrc.h = diff.h * ratio; + dSrc.v = diff.v * ratio; + + } + + else if (fIsRadNOP) + { + + // Tangential only. + + const dng_point_real64 tan = fParams->EvaluateTangential (plane, + rr, + diffNormScaled, + diffNormSqr); + + dSrc.h = diff.h + (fNormRadius * tan.h); + dSrc.v = diff.v + (fNormRadius * tan.v * fPixelScaleVInv); + + } + + else + { + + // Radial and tangential. + + const real64 ratio = fParams->EvaluateRatio (plane, + rr); + + const dng_point_real64 tan = fParams->EvaluateTangential (plane, + rr, + diffNormScaled, + diffNormSqr); + + dSrc.h = fNormRadius * (diffNorm.h * ratio + tan.h); + dSrc.v = fNormRadius * (diffNorm.v * ratio + tan.v * fPixelScaleVInv); + + } + + return fCenter + dSrc; + + } + +/*****************************************************************************/ + +dng_opcode_WarpRectilinear::dng_opcode_WarpRectilinear (const dng_warp_params_rectilinear ¶ms, + uint32 flags) + + : dng_opcode (dngOpcode_WarpRectilinear, + dngVersion_1_3_0_0, + flags) + + , fWarpParams (params) + + { + + if (!params.IsValid ()) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +dng_opcode_WarpRectilinear::dng_opcode_WarpRectilinear (dng_stream &stream) + + : dng_opcode (dngOpcode_WarpRectilinear, + stream, + "WarpRectilinear") + + , fWarpParams () + + { + + // Grab the size in bytes. + + const uint32 bytes = stream.Get_uint32 (); + + // Grab the number of planes to warp. + + fWarpParams.fPlanes = stream.Get_uint32 (); + + // Verify number of planes. + + if (fWarpParams.fPlanes == 0 || + fWarpParams.fPlanes > kMaxColorPlanes) + { + ThrowBadFormat (); + } + + // Verify the size. + + if (bytes != ParamBytes (fWarpParams.fPlanes)) + { + ThrowBadFormat (); + } + + // Read warp parameters for each plane. + + for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++) + { + + fWarpParams.fRadParams [plane][0] = stream.Get_real64 (); + fWarpParams.fRadParams [plane][1] = stream.Get_real64 (); + fWarpParams.fRadParams [plane][2] = stream.Get_real64 (); + fWarpParams.fRadParams [plane][3] = stream.Get_real64 (); + + fWarpParams.fTanParams [plane][0] = stream.Get_real64 (); + fWarpParams.fTanParams [plane][1] = stream.Get_real64 (); + + } + + // Read the image center. + + fWarpParams.fCenter.h = stream.Get_real64 (); + fWarpParams.fCenter.v = stream.Get_real64 (); + + #if qDNGValidate + + if (gVerbose) + { + + fWarpParams.Dump (); + + } + + #endif + + if (!fWarpParams.IsValid ()) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +bool dng_opcode_WarpRectilinear::IsNOP () const + { + + return fWarpParams.IsNOPAll (); + + } + +/*****************************************************************************/ + +bool dng_opcode_WarpRectilinear::IsValidForNegative (const dng_negative &negative) const + { + + return fWarpParams.IsValidForNegative (negative); + + } + +/*****************************************************************************/ + +void dng_opcode_WarpRectilinear::PutData (dng_stream &stream) const + { + + const uint32 bytes = ParamBytes (fWarpParams.fPlanes); + + stream.Put_uint32 (bytes); + + stream.Put_uint32 (fWarpParams.fPlanes); + + for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++) + { + + stream.Put_real64 (fWarpParams.fRadParams [plane][0]); + stream.Put_real64 (fWarpParams.fRadParams [plane][1]); + stream.Put_real64 (fWarpParams.fRadParams [plane][2]); + stream.Put_real64 (fWarpParams.fRadParams [plane][3]); + + stream.Put_real64 (fWarpParams.fTanParams [plane][0]); + stream.Put_real64 (fWarpParams.fTanParams [plane][1]); + + } + + stream.Put_real64 (fWarpParams.fCenter.h); + stream.Put_real64 (fWarpParams.fCenter.v); + + } + +/*****************************************************************************/ + +void dng_opcode_WarpRectilinear::Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image) + { + + #if qDNGValidate + + dng_timer timer ("WarpRectilinear time"); + + #endif + + // Allocate destination image. + + AutoPtr dstImage (host.Make_dng_image (image->Bounds (), + image->Planes (), + image->PixelType ())); + + // Warp the image. + + AutoPtr params (new dng_warp_params_rectilinear (fWarpParams)); + + dng_filter_warp filter (*image, + *dstImage, + negative, + params); + + filter.Initialize (host); + + host.PerformAreaTask (filter, + image->Bounds ()); + + // Return the new image. + + image.Reset (dstImage.Release ()); + + } + +/*****************************************************************************/ + +uint32 dng_opcode_WarpRectilinear::ParamBytes (uint32 planes) + { + + return (1 * (uint32) sizeof (uint32) ) + // Number of planes. + (6 * (uint32) sizeof (real64) * planes) + // Warp coefficients. + (2 * (uint32) sizeof (real64) ); // Optical center. + + } + +/*****************************************************************************/ + +dng_opcode_WarpFisheye::dng_opcode_WarpFisheye (const dng_warp_params_fisheye ¶ms, + uint32 flags) + + : dng_opcode (dngOpcode_WarpFisheye, + dngVersion_1_3_0_0, + flags) + + , fWarpParams (params) + + { + + if (!params.IsValid ()) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +dng_opcode_WarpFisheye::dng_opcode_WarpFisheye (dng_stream &stream) + + : dng_opcode (dngOpcode_WarpFisheye, + stream, + "WarpFisheye") + + , fWarpParams () + + { + + // Grab the size in bytes. + + const uint32 bytes = stream.Get_uint32 (); + + // Grab the number of planes to warp. + + fWarpParams.fPlanes = stream.Get_uint32 (); + + // Verify number of planes. + + if (fWarpParams.fPlanes == 0 || + fWarpParams.fPlanes > kMaxColorPlanes) + { + ThrowBadFormat (); + } + + // Verify the size. + + if (bytes != ParamBytes (fWarpParams.fPlanes)) + { + ThrowBadFormat (); + } + + // Read warp parameters for each plane. + + for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++) + { + + fWarpParams.fRadParams [plane][0] = stream.Get_real64 (); + fWarpParams.fRadParams [plane][1] = stream.Get_real64 (); + fWarpParams.fRadParams [plane][2] = stream.Get_real64 (); + fWarpParams.fRadParams [plane][3] = stream.Get_real64 (); + + } + + // Read the image center. + + fWarpParams.fCenter.h = stream.Get_real64 (); + fWarpParams.fCenter.v = stream.Get_real64 (); + + #if qDNGValidate + + if (gVerbose) + { + + fWarpParams.Dump (); + + } + + #endif + + if (!fWarpParams.IsValid ()) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +bool dng_opcode_WarpFisheye::IsNOP () const + { + + return fWarpParams.IsNOPAll (); + + } + +/*****************************************************************************/ + +bool dng_opcode_WarpFisheye::IsValidForNegative (const dng_negative &negative) const + { + + return fWarpParams.IsValidForNegative (negative); + + } + +/*****************************************************************************/ + +void dng_opcode_WarpFisheye::PutData (dng_stream &stream) const + { + + const uint32 bytes = ParamBytes (fWarpParams.fPlanes); + + stream.Put_uint32 (bytes); + + // Write the number of planes. + + stream.Put_uint32 (fWarpParams.fPlanes); + + // Write the warp coefficients. + + for (uint32 plane = 0; plane < fWarpParams.fPlanes; plane++) + { + + stream.Put_real64 (fWarpParams.fRadParams [plane][0]); + stream.Put_real64 (fWarpParams.fRadParams [plane][1]); + stream.Put_real64 (fWarpParams.fRadParams [plane][2]); + stream.Put_real64 (fWarpParams.fRadParams [plane][3]); + + } + + // Write the optical center. + + stream.Put_real64 (fWarpParams.fCenter.h); + stream.Put_real64 (fWarpParams.fCenter.v); + + } + +/*****************************************************************************/ + +void dng_opcode_WarpFisheye::Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image) + { + + #if qDNGValidate + + dng_timer timer ("WarpFisheye time"); + + #endif + + // Allocate destination image. + + AutoPtr dstImage (host.Make_dng_image (image->Bounds (), + image->Planes (), + image->PixelType ())); + + // Warp the image. + + AutoPtr params (new dng_warp_params_fisheye (fWarpParams)); + + dng_filter_warp filter (*image, + *dstImage, + negative, + params); + + filter.Initialize (host); + + host.PerformAreaTask (filter, + image->Bounds ()); + + // Return the new image. + + image.Reset (dstImage.Release ()); + + } + +/*****************************************************************************/ + +uint32 dng_opcode_WarpFisheye::ParamBytes (uint32 planes) + { + + return (1 * (uint32) sizeof (uint32) ) + // Number of planes. + (4 * (uint32) sizeof (real64) * planes) + // Warp coefficients. + (2 * (uint32) sizeof (real64) ); // Optical center. + + } + +/*****************************************************************************/ + +dng_vignette_radial_params::dng_vignette_radial_params () + + : fParams (kNumTerms) + , fCenter (0.5, 0.5) + + { + + } + +/*****************************************************************************/ + +dng_vignette_radial_params::dng_vignette_radial_params (const std::vector ¶ms, + const dng_point_real64 ¢er) + + : fParams (params) + , fCenter (center) + + { + + } + +/*****************************************************************************/ + +bool dng_vignette_radial_params::IsNOP () const + { + + for (uint32 i = 0; i < fParams.size (); i++) + { + + if (fParams [i] != 0.0) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_vignette_radial_params::IsValid () const + { + + if (fParams.size () != kNumTerms) + { + return false; + } + + if (fCenter.h < 0.0 || + fCenter.h > 1.0 || + fCenter.v < 0.0 || + fCenter.v > 1.0) + { + return false; + } + + return true; + + } + +/*****************************************************************************/ + +void dng_vignette_radial_params::Dump () const + { + + #if qDNGValidate + + printf (" Radial vignette params: "); + + for (uint32 i = 0; i < fParams.size (); i++) + { + + printf ("%s%.6lf", + (i == 0) ? "" : ", ", + fParams [i]); + + } + + printf ("\n"); + + printf (" Optical center:\n" + " h = %.6lf\n" + " v = %.6lf\n", + fCenter.h, + fCenter.v); + + #endif + + } + +/*****************************************************************************/ + +class dng_vignette_radial_function: public dng_1d_function + { + + protected: + + const dng_vignette_radial_params fParams; + + public: + + explicit dng_vignette_radial_function (const dng_vignette_radial_params ¶ms) + + : fParams (params) + + { + + } + + // x represents r^2, where r is the normalized Euclidean distance from the + // optical center to a pixel. r lies in [0,1], so r^2 (and hence x) also lies + // in [0,1]. + + virtual real64 Evaluate (real64 x) const + { + + DNG_REQUIRE (fParams.fParams.size () == dng_vignette_radial_params::kNumTerms, + "Bad number of vignette opcode coefficients."); + + real64 sum = 0.0; + + const std::vector &v = fParams.fParams; + + for (std::vector::const_reverse_iterator i = v.rbegin (); i != v.rend (); i++) + { + sum = x * ((*i) + sum); + } + + sum += 1.0; + + return sum; + + } + + }; + +/*****************************************************************************/ + +dng_opcode_FixVignetteRadial::dng_opcode_FixVignetteRadial (const dng_vignette_radial_params ¶ms, + uint32 flags) + + : dng_inplace_opcode (dngOpcode_FixVignetteRadial, + dngVersion_1_3_0_0, + flags) + + , fParams (params) + + , fImagePlanes (1) + + , fSrcOriginH (0) + , fSrcOriginV (0) + + , fSrcStepH (0) + , fSrcStepV (0) + + , fTableInputBits (0) + , fTableOutputBits (0) + + , fGainTable () + + { + + if (!params.IsValid ()) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +dng_opcode_FixVignetteRadial::dng_opcode_FixVignetteRadial (dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_FixVignetteRadial, + stream, + "FixVignetteRadial") + + , fParams () + + , fImagePlanes (1) + + , fSrcOriginH (0) + , fSrcOriginV (0) + + , fSrcStepH (0) + , fSrcStepV (0) + + , fTableInputBits (0) + , fTableOutputBits (0) + + , fGainTable () + + { + + // Grab the size in bytes. + + const uint32 bytes = stream.Get_uint32 (); + + // Verify the size. + + if (bytes != ParamBytes ()) + { + ThrowBadFormat (); + } + + // Read vignette coefficients. + + fParams.fParams = std::vector (dng_vignette_radial_params::kNumTerms); + + for (uint32 i = 0; i < dng_vignette_radial_params::kNumTerms; i++) + { + fParams.fParams [i] = stream.Get_real64 (); + } + + // Read the image center. + + fParams.fCenter.h = stream.Get_real64 (); + fParams.fCenter.v = stream.Get_real64 (); + + // Debug. + + #if qDNGValidate + + if (gVerbose) + { + + fParams.Dump (); + + } + + #endif + + if (!fParams.IsValid ()) + { + ThrowBadFormat (); + } + + } + +/*****************************************************************************/ + +bool dng_opcode_FixVignetteRadial::IsNOP () const + { + + return fParams.IsNOP (); + + } + +/*****************************************************************************/ + +bool dng_opcode_FixVignetteRadial::IsValidForNegative (const dng_negative & /* negative */) const + { + + return fParams.IsValid (); + + } + +/*****************************************************************************/ + +void dng_opcode_FixVignetteRadial::PutData (dng_stream &stream) const + { + + const uint32 bytes = ParamBytes (); + + stream.Put_uint32 (bytes); + + DNG_REQUIRE (fParams.fParams.size () == dng_vignette_radial_params::kNumTerms, + "Bad number of vignette opcode coefficients."); + + for (uint32 i = 0; i < dng_vignette_radial_params::kNumTerms; i++) + { + stream.Put_real64 (fParams.fParams [i]); + } + + stream.Put_real64 (fParams.fCenter.h); + stream.Put_real64 (fParams.fCenter.v); + + } + +/*****************************************************************************/ + +void dng_opcode_FixVignetteRadial::Prepare (dng_negative &negative, + uint32 threadCount, + const dng_point &tileSize, + const dng_rect &imageBounds, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator &allocator) + { + + // This opcode is restricted to 32-bit images. + + if (bufferPixelType != ttFloat) + { + ThrowBadFormat (); + } + + // Sanity check number of planes. + + DNG_ASSERT (imagePlanes >= 1 && imagePlanes <= kMaxColorPlanes, + "Bad number of planes."); + + if (imagePlanes < 1 || imagePlanes > kMaxColorPlanes) + { + ThrowProgramError (); + } + + fImagePlanes = imagePlanes; + + // Set the vignette correction curve. + + const dng_vignette_radial_function curve (fParams); + + // Grab the destination image area. + + const dng_rect_real64 bounds (imageBounds); + + // Determine the optical center and maximum radius in pixel coordinates. + + const dng_point_real64 centerPixel (Lerp_real64 (bounds.t, + bounds.b, + fParams.fCenter.v), + + Lerp_real64 (bounds.l, + bounds.r, + fParams.fCenter.h)); + + const real64 pixelScaleV = 1.0 / negative.PixelAspectRatio (); + + const real64 maxRadius = hypot (Max_real64 (Abs_real64 (centerPixel.v - bounds.t), + Abs_real64 (centerPixel.v - bounds.b)) * pixelScaleV, + + Max_real64 (Abs_real64 (centerPixel.h - bounds.l), + Abs_real64 (centerPixel.h - bounds.r))); + + const dng_point_real64 radius (maxRadius, + maxRadius); + + // Find origin and scale. + + const real64 pixelScaleH = 1.0; + + fSrcOriginH = Real64ToFixed64 (-centerPixel.h * pixelScaleH / radius.h); + fSrcOriginV = Real64ToFixed64 (-centerPixel.v * pixelScaleV / radius.v); + + fSrcStepH = Real64ToFixed64 (pixelScaleH / radius.h); + fSrcStepV = Real64ToFixed64 (pixelScaleV / radius.v); + + // Adjust for pixel centers. + + fSrcOriginH += fSrcStepH >> 1; + fSrcOriginV += fSrcStepV >> 1; + + // Evaluate 32-bit vignette correction table. + + dng_1d_table table32; + + table32.Initialize (allocator, + curve, + false); + + // Find maximum scale factor. + + const real64 maxScale = Max_real32 (table32.Interpolate (0.0f), + table32.Interpolate (1.0f)); + + // Find table input bits. + + fTableInputBits = 16; + + // Find table output bits. + + fTableOutputBits = 15; + + while ((1 << fTableOutputBits) * maxScale > 65535.0) + { + fTableOutputBits--; + } + + // Allocate 16-bit scale table. + + const uint32 tableEntries = (1 << fTableInputBits) + 1; + + fGainTable.Reset (allocator.Allocate (tableEntries * (uint32) sizeof (uint16))); + + uint16 *table16 = fGainTable->Buffer_uint16 (); + + // Interpolate 32-bit table into 16-bit table. + + const real32 scale0 = 1.0f / (1 << fTableInputBits ); + const real32 scale1 = 1.0f * (1 << fTableOutputBits); + + for (uint32 index = 0; index < tableEntries; index++) + { + + real32 x = index * scale0; + + real32 y = table32.Interpolate (x) * scale1; + + table16 [index] = (uint16) Round_uint32 (y); + + } + + // Prepare vignette mask buffers. + + { + + const uint32 pixelType = ttShort; + const uint32 pixelSize = TagTypeSize (pixelType); + + const uint32 bufferSize = tileSize.v * + RoundUpForPixelSize (tileSize.h, pixelSize) * + pixelSize * + imagePlanes; + + for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++) + { + + fMaskBuffers [threadIndex] . Reset (allocator.Allocate (bufferSize)); + + } + + } + + } + +/*****************************************************************************/ + +void dng_opcode_FixVignetteRadial::ProcessArea (dng_negative & /* negative */, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + // Setup mask pixel buffer. + + dng_pixel_buffer maskPixelBuffer; + + maskPixelBuffer.fArea = dstArea; + + maskPixelBuffer.fPlane = 0; + maskPixelBuffer.fPlanes = fImagePlanes; + + maskPixelBuffer.fPixelType = ttShort; + maskPixelBuffer.fPixelSize = TagTypeSize (maskPixelBuffer.fPixelType); + + maskPixelBuffer.fPlaneStep = RoundUpForPixelSize (dstArea.W (), + maskPixelBuffer.fPixelSize); + + maskPixelBuffer.fRowStep = maskPixelBuffer.fPlaneStep * maskPixelBuffer.fPlanes; + + maskPixelBuffer.fData = fMaskBuffers [threadIndex]->Buffer (); + + // Compute mask. + + DoVignetteMask16 (maskPixelBuffer.DirtyPixel_uint16 (dstArea.t, dstArea.l), + dstArea.H (), + dstArea.W (), + maskPixelBuffer.RowStep (), + fSrcOriginH + fSrcStepH * dstArea.l, + fSrcOriginV + fSrcStepV * dstArea.t, + fSrcStepH, + fSrcStepV, + fTableInputBits, + fGainTable->Buffer_uint16 ()); + + // Apply mask. + + DoVignette32 (buffer.DirtyPixel_real32 (dstArea.t, dstArea.l), + maskPixelBuffer.ConstPixel_uint16 (dstArea.t, dstArea.l), + dstArea.H (), + dstArea.W (), + fImagePlanes, + buffer.RowStep (), + buffer.PlaneStep (), + maskPixelBuffer.RowStep (), + fTableOutputBits); + + } + +/*****************************************************************************/ + +uint32 dng_opcode_FixVignetteRadial::ParamBytes () + { + + const uint32 N = dng_vignette_radial_params::kNumTerms; + + return ((N * sizeof (real64)) + // Vignette coefficients. + (2 * sizeof (real64))); // Optical center. + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_lens_correction.h b/source/lib/dng_sdk/dng_lens_correction.h new file mode 100644 index 0000000..ae1ca6b --- /dev/null +++ b/source/lib/dng_sdk/dng_lens_correction.h @@ -0,0 +1,638 @@ +/*****************************************************************************/ +// Copyright 2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_lens_correction.h#2 $ */ +/* $DateTime: 2012/08/02 06:09:06 $ */ +/* $Change: 841096 $ */ +/* $Author: erichan $ */ + +/** \file + * Opcodes to fix lens aberrations such as geometric distortion, lateral chromatic + * aberration, and vignetting (peripheral illumination falloff). + */ + +/*****************************************************************************/ + +#ifndef __dng_lens_correction__ +#define __dng_lens_correction__ + +/*****************************************************************************/ + +#include "dng_1d_function.h" +#include "dng_matrix.h" +#include "dng_opcodes.h" +#include "dng_pixel_buffer.h" +#include "dng_point.h" +#include "dng_resample.h" +#include "dng_sdk_limits.h" + +#include + +/*****************************************************************************/ + +/// \brief Abstract base class holding common warp opcode parameters (e.g., +/// number of planes, optical center) and common warp routines. + +class dng_warp_params + { + + public: + + // Number of planes to be warped. Must be either 1 or equal to the + // number of planes of the image to be processed. If set to 1, then a + // single set of warp parameters applies to all planes of the image. + // fPlanes must be at least 1 and no greater than kMaxColorPlanes (see + // dng_sdk_limits.h). + + uint32 fPlanes; + + // The optical center of the lens in normalized [0,1] coordinates with + // respect to the image's active area. For example, a value of (0.5, + // 0.5) indicates that the optical center of the lens is at the center + // of the image's active area. A normalized radius of 1.0 corresponds to + // the distance from fCenter to the farthest corner of the image's + // active area. Each component of fCenter must lie in the range [0,1]. + + dng_point_real64 fCenter; + + public: + + /// Create empty (invalid) warp parameters. + + dng_warp_params (); + + /// Create warp parameters with specified number of planes and image + /// center. + /// + /// \param planes The number of planes of parameters specified: It must + /// be either 1 or equal to the number of planes of the image to be + /// processed. + /// + /// \param fCenter The image center in relative coordinates. + + dng_warp_params (uint32 planes, + const dng_point_real64 &fCenter); + + virtual ~dng_warp_params (); + + /// Is the entire correction a NOP for all planes? + + virtual bool IsNOPAll () const; + + /// Is the entire correction a NOP for the specified plane? + + virtual bool IsNOP (uint32 plane) const; + + /// Is the radial correction a NOP for all planes? + + virtual bool IsRadNOPAll () const; + + /// Is the radial correction a NOP for the specified plane? + + virtual bool IsRadNOP (uint32 plane) const; + + /// Is the tangential correction a NOP for all planes? + + virtual bool IsTanNOPAll () const; + + /// Is the tangential correction a NOP for the specified plane? + + virtual bool IsTanNOP (uint32 plane) const; + + /// Do these warp params appear valid? + + virtual bool IsValid () const; + + /// Are these warp params valid for the specified negative? + + virtual bool IsValidForNegative (const dng_negative &negative) const; + + /// Propagate warp parameters from first plane to all other planes. + + virtual void PropagateToAllPlanes (uint32 totalPlanes) = 0; + + /// Evaluate the 1D radial warp function for the specified plane. + /// Parameter r is the destination (i.e., corrected) normalized radius, + /// i.e., the normalized Euclidean distance between a corrected pixel + /// position and the optical center in the image. r lies in the range + /// [0,1]. The returned result is non-negative. + + virtual real64 Evaluate (uint32 plane, + real64 r) const = 0; + + /// Compute and return the inverse of Evaluate () above. The base + /// implementation uses Newton's method to perform the inversion. + /// Parameter r is the source (i.e., uncorrected) normalized radius, + /// i.e., normalized Euclidean distance between a corrected pixel + /// position and the optical center in the image. Both r and the + /// computed result are non-negative. + + virtual real64 EvaluateInverse (uint32 plane, + real64 r) const; + + /// Evaluate the 1D radial warp ratio function for the specified plane. + /// Parameter r2 is the square of the destination (i.e., corrected) + /// normalized radius, i.e., the square of the normalized Euclidean + /// distance between a corrected pixel position and the optical center + /// in the image. r2 must lie in the range [0,1]. Note that this is + /// different than the Evaluate () function, above, in that the argument + /// to EvaluateRatio () is the square of the radius, not the radius + /// itself. The returned result is non-negative. Mathematically, + /// EvaluateRatio (r * r) is the same as Evaluate (r) / r. + + virtual real64 EvaluateRatio (uint32 plane, + real64 r2) const = 0; + + /// Evaluate the 2D tangential warp for the specified plane. Parameter + /// r2 is the square of the destination (i.e., corrected) normalized + /// radius, i.e., the square of the normalized Euclidean distance + /// between a corrected pixel position P and the optical center in the + /// image. r2 must lie in the range [0,1]. diff contains the vertical + /// and horizontal Euclidean distances (in pixels) between P and the + /// optical center. diff2 contains the squares of the vertical and + /// horizontal Euclidean distances (in pixels) between P and the optical + /// center. The returned result is the tangential warp offset, measured + /// in pixels. + + virtual dng_point_real64 EvaluateTangential (uint32 plane, + real64 r2, + const dng_point_real64 &diff, + const dng_point_real64 &diff2) const = 0; + + /// Evaluate the 2D tangential warp for the specified plane. diff + /// contains the vertical and horizontal Euclidean distances (in pixels) + /// between the destination (i.e., corrected) pixel position and the + /// optical center in the image. The returned result is the tangential + /// warp offset, measured in pixels. + + dng_point_real64 EvaluateTangential2 (uint32 plane, + const dng_point_real64 &diff) const; + + /// Evaluate the 2D tangential warp for the specified plane. Parameter + /// r2 is the square of the destination (i.e., corrected) normalized + /// radius, i.e., the square of the normalized Euclidean distance + /// between a corrected pixel position P and the optical center in the + /// image. r2 must lie in the range [0,1]. diff contains the vertical + /// and horizontal Euclidean distances (in pixels) between P and the + /// optical center. The returned result is the tangential warp offset, + /// measured in pixels. + + dng_point_real64 EvaluateTangential3 (uint32 plane, + real64 r2, + const dng_point_real64 &diff) const; + + /// Compute and return the maximum warped radius gap. Let D be a + /// rectangle in a destination (corrected) image. Let rDstFar and + /// rDstNear be the farthest and nearest points to the image center, + /// respectively. Then the specified parameter maxDstGap is the + /// Euclidean distance between rDstFar and rDstNear. Warp D through this + /// warp function to a closed and bounded (generally not rectangular) + /// region S. Let rSrcfar and rSrcNear be the farthest and nearest + /// points to the image center, respectively. This routine returns a + /// value that is at least (rSrcFar - rSrcNear). + + virtual real64 MaxSrcRadiusGap (real64 maxDstGap) const = 0; + + /// Compute and return the maximum warped tangential gap. minDst is the + /// top-left pixel of the image in normalized pixel coordinates. maxDst + /// is the bottom-right pixel of the image in normalized pixel + /// coordinates. MaxSrcTanGap () computes the maximum absolute shift in + /// normalized pixels in the horizontal and vertical directions that can + /// occur as a result of the tangential warp. + + virtual dng_point_real64 MaxSrcTanGap (dng_point_real64 minDst, + dng_point_real64 maxDst) const = 0; + + /// Debug parameters. + + virtual void Dump () const; + + }; + +/*****************************************************************************/ + +/// \brief Warp parameters for pinhole perspective rectilinear (not fisheye) +/// camera model. Supports radial and tangential (decentering) distortion +/// correction parameters. +/// +/// Note the restrictions described below. + +class dng_warp_params_rectilinear: public dng_warp_params + { + + public: + + // Radial and tangential polynomial coefficients. These define a warp + // from corrected pixel coordinates (xDst, yDst) to uncorrected pixel + // coordinates (xSrc, ySrc) for each plane P as follows: + // + // Let kr0 = fRadParams [P][0] + // kr1 = fRadParams [P][1] + // kr2 = fRadParams [P][2] + // kr3 = fRadParams [P][3] + // + // kt0 = fTanParams [P][0] + // kt1 = fTanParams [P][1] + // + // Let (xCenter, yCenter) be the optical image center (see fCenter, + // below) expressed in pixel coordinates. Let maxDist be the Euclidean + // distance (in pixels) from (xCenter, yCenter) to the farthest image + // corner. + // + // First, compute the normalized distance of the corrected pixel + // position (xDst, yDst) from the image center: + // + // dx = (xDst - xCenter) / maxDist + // dy = (yDst - yCenter) / maxDist + // + // r^2 = dx^2 + dy^2 + // + // Compute the radial correction term: + // + // ratio = kr0 + (kr1 * r^2) + (kr2 * r^4) + (kr3 * r^6) + // + // dxRad = dx * ratio + // dyRad = dy * ratio + // + // Compute the tangential correction term: + // + // dxTan = (2 * kt0 * dx * dy) + kt1 * (r^2 + 2 * dx^2) + // dyTan = (2 * kt1 * dx * dy) + kt0 * (r^2 + 2 * dy^2) + // + // Compute the uncorrected pixel position (xSrc, ySrc): + // + // xSrc = xCenter + (dxRad + dxTan) * maxDist + // ySrc = yCenter + (dyRad + dyTan) * maxDist + // + // Mathematical definitions and restrictions: + // + // Let { xSrc, ySrc } = f (xDst, yDst) be the warp function defined + // above. + // + // Let xSrc = fx (xDst, yDst) be the x-component of the warp function. + // Let ySrc = fy (xDst, yDst) be the y-component of the warp function. + // + // f (x, y) must be an invertible function. + // + // fx (x, y) must be an increasing function of x. + // fy (x, y) must be an increasing function of x. + // + // The parameters kr0, kr1, kr2, and kr3 must define an increasing + // radial warp function. Specifically, let w (r) be the radial warp + // function: + // + // w (r) = (kr0 * r) + (kr1 * r^3) + (kr2 * r^5) + (kr3 * r^7). + // + // w (r) must be an increasing function. + + dng_vector fRadParams [kMaxColorPlanes]; + dng_vector fTanParams [kMaxColorPlanes]; + + public: + + /// Create empty (invalid) rectilinear warp parameters. + + dng_warp_params_rectilinear (); + + /// Create rectilinear warp parameters with the specified number of + /// planes, radial component terms, tangential component terms, and + /// image center in relative coordinates. + + dng_warp_params_rectilinear (uint32 planes, + const dng_vector radParams [], + const dng_vector tanParams [], + const dng_point_real64 &fCenter); + + virtual ~dng_warp_params_rectilinear (); + + // Overridden methods. + + virtual bool IsRadNOP (uint32 plane) const; + + virtual bool IsTanNOP (uint32 plane) const; + + virtual bool IsValid () const; + + virtual void PropagateToAllPlanes (uint32 totalPlanes); + + virtual real64 Evaluate (uint32 plane, + real64 r) const; + + virtual real64 EvaluateRatio (uint32 plane, + real64 r2) const; + + virtual dng_point_real64 EvaluateTangential (uint32 plane, + real64 r2, + const dng_point_real64 &diff, + const dng_point_real64 &diff2) const; + + virtual real64 MaxSrcRadiusGap (real64 maxDstGap) const; + + virtual dng_point_real64 MaxSrcTanGap (dng_point_real64 minDst, + dng_point_real64 maxDst) const; + + virtual void Dump () const; + + }; + +/*****************************************************************************/ + +/// \brief Warp parameters for fisheye camera model (radial component only). +/// Note the restrictions described below. + +class dng_warp_params_fisheye: public dng_warp_params + { + + public: + + // Radial warp coefficients. These define a warp from corrected pixel + // coordinates (xDst, yDst) to uncorrected pixel coordinates (xSrc, + // ySrc) for each plane P as follows: + // + // Let kr0 = fRadParams [P][0] + // kr1 = fRadParams [P][1] + // kr2 = fRadParams [P][2] + // kr3 = fRadParams [P][3] + // + // Let (xCenter, yCenter) be the optical image center (see fCenter, + // below) expressed in pixel coordinates. Let maxDist be the Euclidean + // distance (in pixels) from (xCenter, yCenter) to the farthest image + // corner. + // + // First, compute the normalized distance of the corrected pixel + // position (xDst, yDst) from the image center: + // + // dx = (xDst - xCenter) / maxDist + // dy = (yDst - yCenter) / maxDist + // + // r = sqrt (dx^2 + dy^2) + // + // Compute the radial correction term: + // + // t = atan (r) + // + // rWarp = (kr0 * t) + (kr1 * t^3) + (kr2 * t^5) + (kr3 * t^7) + // + // ratio = rWarp / r + // + // dxRad = dx * ratio + // dyRad = dy * ratio + // + // Compute the uncorrected pixel position (xSrc, ySrc): + // + // xSrc = xCenter + (dxRad * maxDist) + // ySrc = yCenter + (dyRad * maxDist) + // + // The parameters kr0, kr1, kr2, and kr3 must define an increasing + // radial warp function. Specifically, let w (r) be the radial warp + // function: + // + // t = atan (r) + // + // w (r) = (kr0 * t) + (kr1 * t^3) + (kr2 * t^5) + (kr3 * t^7). + // + // w (r) must be an increasing function. + + dng_vector fRadParams [kMaxColorPlanes]; + + public: + + /// Create empty (invalid) fisheye warp parameters. + + dng_warp_params_fisheye (); + + /// Create rectilinear warp parameters with the specified number of + /// planes, radial component terms, and image center in relative + /// coordinates. + + dng_warp_params_fisheye (uint32 planes, + const dng_vector radParams [], + const dng_point_real64 &fCenter); + + virtual ~dng_warp_params_fisheye (); + + // Overridden methods. + + virtual bool IsRadNOP (uint32 plane) const; + + virtual bool IsTanNOP (uint32 plane) const; + + virtual bool IsValid () const; + + virtual void PropagateToAllPlanes (uint32 totalPlanes); + + virtual real64 Evaluate (uint32 plane, + real64 r) const; + + virtual real64 EvaluateRatio (uint32 plane, + real64 r2) const; + + virtual dng_point_real64 EvaluateTangential (uint32 plane, + real64 r2, + const dng_point_real64 &diff, + const dng_point_real64 &diff2) const; + + virtual real64 MaxSrcRadiusGap (real64 maxDstGap) const; + + virtual dng_point_real64 MaxSrcTanGap (dng_point_real64 minDst, + dng_point_real64 maxDst) const; + + virtual void Dump () const; + + }; + +/*****************************************************************************/ + +/// \brief Warp opcode for pinhole perspective (rectilinear) camera model. + +class dng_opcode_WarpRectilinear: public dng_opcode + { + + protected: + + dng_warp_params_rectilinear fWarpParams; + + public: + + dng_opcode_WarpRectilinear (const dng_warp_params_rectilinear ¶ms, + uint32 flags); + + explicit dng_opcode_WarpRectilinear (dng_stream &stream); + + // Overridden methods. + + virtual bool IsNOP () const; + + virtual bool IsValidForNegative (const dng_negative &negative) const; + + virtual void PutData (dng_stream &stream) const; + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + protected: + + static uint32 ParamBytes (uint32 planes); + + }; + +/*****************************************************************************/ + +/// \brief Warp opcode for fisheye camera model. + +class dng_opcode_WarpFisheye: public dng_opcode + { + + protected: + + dng_warp_params_fisheye fWarpParams; + + public: + + dng_opcode_WarpFisheye (const dng_warp_params_fisheye ¶ms, + uint32 flags); + + explicit dng_opcode_WarpFisheye (dng_stream &stream); + + // Overridden methods. + + virtual bool IsNOP () const; + + virtual bool IsValidForNegative (const dng_negative &negative) const; + + virtual void PutData (dng_stream &stream) const; + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + protected: + + static uint32 ParamBytes (uint32 planes); + + }; + +/*****************************************************************************/ + +/// \brief Radially-symmetric vignette (peripheral illuminational falloff) +/// correction parameters. + +class dng_vignette_radial_params + { + + public: + + static const uint32 kNumTerms = 5; + + public: + + // Let v be an uncorrected pixel value of a pixel p in linear space. + // + // Let r be the Euclidean distance between p and the optical center. + // + // Compute corrected pixel value v' = v * g, where g is the gain. + // + // Let k0 = fParams [0] + // Let k1 = fParams [1] + // Let k2 = fParams [2] + // Let k3 = fParams [3] + // Let k4 = fParams [4] + // + // Gain g = 1 + (k0 * r^2) + (k1 * r^4) + (k2 * r^6) + (k3 * r^8) + (k4 * r^10) + + std::vector fParams; + + dng_point_real64 fCenter; + + public: + + dng_vignette_radial_params (); + + dng_vignette_radial_params (const std::vector ¶ms, + const dng_point_real64 ¢er); + + bool IsNOP () const; + + bool IsValid () const; + + // For debugging. + + void Dump () const; + + }; + +/*****************************************************************************/ + +/// \brief Radially-symmetric lens vignette correction opcode. + +class dng_opcode_FixVignetteRadial: public dng_inplace_opcode + { + + protected: + + dng_vignette_radial_params fParams; + + uint32 fImagePlanes; + + int64 fSrcOriginH; + int64 fSrcOriginV; + + int64 fSrcStepH; + int64 fSrcStepV; + + uint32 fTableInputBits; + uint32 fTableOutputBits; + + AutoPtr fGainTable; + + AutoPtr fMaskBuffers [kMaxMPThreads]; + + public: + + dng_opcode_FixVignetteRadial (const dng_vignette_radial_params ¶ms, + uint32 flags); + + explicit dng_opcode_FixVignetteRadial (dng_stream &stream); + + virtual bool IsNOP () const; + + virtual bool IsValidForNegative (const dng_negative &) const; + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 /* imagePixelType */) + { + return ttFloat; + } + + virtual void Prepare (dng_negative &negative, + uint32 threadCount, + const dng_point &tileSize, + const dng_rect &imageBounds, + uint32 imagePlanes, + uint32 bufferPixelType, + dng_memory_allocator &allocator); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + protected: + + static uint32 ParamBytes (); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_linearization_info.cpp b/source/lib/dng_sdk/dng_linearization_info.cpp new file mode 100644 index 0000000..1c1309b --- /dev/null +++ b/source/lib/dng_sdk/dng_linearization_info.cpp @@ -0,0 +1,1517 @@ +/*****************************************************************************/ +// Copyright 2006-2011 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_linearization_info.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_linearization_info.h" + +#include "dng_area_task.h" +#include "dng_exceptions.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_info.h" +#include "dng_negative.h" +#include "dng_pixel_buffer.h" +#include "dng_tag_types.h" +#include "dng_tile_iterator.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +class dng_linearize_plane + { + + private: + + const dng_image & fSrcImage; + dng_image & fDstImage; + + uint32 fPlane; + + dng_rect fActiveArea; + + uint32 fSrcPixelType; + uint32 fDstPixelType; + + bool fReal32; + + real32 fScale; + + AutoPtr fScale_buffer; + + uint32 fBlack_2D_rows; + uint32 fBlack_2D_cols; + + AutoPtr fBlack_2D_buffer; + + uint32 fBlack_1D_rows; + + AutoPtr fBlack_1D_buffer; + + public: + + dng_linearize_plane (dng_host &host, + dng_linearization_info &info, + const dng_image &srcImage, + dng_image &dstImage, + uint32 plane); + + ~dng_linearize_plane (); + + void Process (const dng_rect &tile); + + }; + +/*****************************************************************************/ + +dng_linearize_plane::dng_linearize_plane (dng_host &host, + dng_linearization_info &info, + const dng_image &srcImage, + dng_image &dstImage, + uint32 plane) + + : fSrcImage (srcImage) + , fDstImage (dstImage) + , fPlane (plane) + , fActiveArea (info.fActiveArea) + , fSrcPixelType (srcImage.PixelType ()) + , fDstPixelType (dstImage.PixelType ()) + , fReal32 (false) + , fScale (0.0f) + , fScale_buffer () + , fBlack_2D_rows (0) + , fBlack_2D_cols (0) + , fBlack_2D_buffer () + , fBlack_1D_rows (0) + , fBlack_1D_buffer () + + { + + uint32 j; + uint32 k; + + // Make sure the source pixel type is supported. + + if (fSrcPixelType != ttByte && + fSrcPixelType != ttShort && + fSrcPixelType != ttLong && + fSrcPixelType != ttFloat) + { + + DNG_REPORT ("Unsupported source pixel type"); + + ThrowProgramError (); + + } + + if (fDstPixelType != ttShort && + fDstPixelType != ttFloat) + { + + DNG_REPORT ("Unsupported destination pixel type"); + + ThrowProgramError (); + + } + + if (fSrcPixelType == ttFloat && + fDstPixelType != ttFloat) + { + + DNG_REPORT ("Cannot convert floating point stage1 to non-floating stage2"); + + ThrowProgramError (); + + } + + // Are we using floating point math? + + fReal32 = (fSrcPixelType == ttLong || + fDstPixelType == ttFloat); + + // Find the scale for this plane. + + real64 maxBlack = info.MaxBlackLevel (plane); + + real64 minRange = info.fWhiteLevel [plane] - maxBlack; + + if (minRange <= 0.0) + { + ThrowBadFormat (); + } + + real64 scale = 1.0 / minRange; + + fScale = (real32) scale; + + // Calculate two-dimensional black pattern, if any. + + if (info.fBlackDeltaH.Get ()) + { + + fBlack_2D_rows = info.fBlackLevelRepeatRows; + fBlack_2D_cols = info.fActiveArea.W (); + + } + + else if (info.fBlackLevelRepeatCols > 1) + { + + fBlack_2D_rows = info.fBlackLevelRepeatRows; + fBlack_2D_cols = info.fBlackLevelRepeatCols; + + } + + if (fBlack_2D_rows) + { + + fBlack_2D_buffer.Reset (host.Allocate (fBlack_2D_rows * fBlack_2D_cols * 4)); + + for (j = 0; j < fBlack_2D_rows; j++) + { + + for (k = 0; k < fBlack_2D_cols; k++) + { + + real64 x = info.fBlackLevel [j] + [k % info.fBlackLevelRepeatCols] + [plane]; + + if (info.fBlackDeltaH.Get ()) + { + + x += info.fBlackDeltaH->Buffer_real64 () [k]; + + } + + x *= scale; + + uint32 index = j * fBlack_2D_cols + k; + + if (fReal32) + { + + fBlack_2D_buffer->Buffer_real32 () [index] = (real32) x; + + } + + else + { + + x *= 0x0FFFF * 256.0; + + int32 y = Round_int32 (x); + + fBlack_2D_buffer->Buffer_int32 () [index] = y; + + } + + } + + } + + } + + // Calculate one-dimensional (per row) black pattern, if any. + + if (info.fBlackDeltaV.Get ()) + { + + fBlack_1D_rows = info.fActiveArea.H (); + + } + + else if (fBlack_2D_rows == 0 && + (info.fBlackLevelRepeatRows > 1 || fSrcPixelType != ttShort)) + { + + fBlack_1D_rows = info.fBlackLevelRepeatRows; + + } + + if (fBlack_1D_rows) + { + + fBlack_1D_buffer.Reset (host.Allocate (fBlack_1D_rows * 4)); + + bool allZero = true; + + for (j = 0; j < fBlack_1D_rows; j++) + { + + real64 x = 0.0; + + if (fBlack_2D_rows == 0) + { + + x = info.fBlackLevel [j % info.fBlackLevelRepeatRows] + [0] + [plane]; + + } + + if (info.fBlackDeltaV.Get ()) + { + + x += info.fBlackDeltaV->Buffer_real64 () [j]; + + } + + allZero = allZero && (x == 0.0); + + x *= scale; + + if (fReal32) + { + + fBlack_1D_buffer->Buffer_real32 () [j] = (real32) x; + + } + + else + { + + x *= 0x0FFFF * 256.0; + + int32 y = Round_int32 (x); + + fBlack_1D_buffer->Buffer_int32 () [j] = y; + + } + + } + + if (allZero) + { + + fBlack_1D_rows = 0; + + fBlack_1D_buffer.Reset (); + + } + + } + + // Calculate scale table, if any. + + if (fSrcPixelType != ttLong && + fSrcPixelType != ttFloat) + { + + // Find linearization table, if any. + + uint16 *lut = NULL; + + uint32 lutEntries = 0; + + if (info.fLinearizationTable.Get ()) + { + + lut = info.fLinearizationTable->Buffer_uint16 (); + + lutEntries = info.fLinearizationTable->LogicalSize () >> 1; + + } + + // If the black level does not vary from pixel to pixel, then + // the entire process can be a single LUT. + + if (fBlack_1D_rows == 0 && + fBlack_2D_rows == 0) + { + + fScale_buffer.Reset (host.Allocate (0x10000 * + TagTypeSize (fDstPixelType))); + + for (j = 0; j < 0x10000; j++) + { + + uint32 x = j; + + // Apply linearization table, if any. + + if (lut) + { + + x = Min_uint32 (x, lutEntries - 1); + + x = lut [x]; + + } + + // Subtract constant black level. + + real64 y = x - info.fBlackLevel [0] [0] [plane]; + + // Apply scale. + + y *= scale; + + // We can burn in the clipping also. + + y = Pin_real64 (0.0, y, 1.0); + + // Store output value in table. + + if (fDstPixelType == ttShort) + { + + uint16 z = (uint16) Round_uint32 (y * 0x0FFFF); + + fScale_buffer->Buffer_uint16 () [j] = z; + + } + + else + { + + fScale_buffer->Buffer_real32 () [j] = (real32) y; + + } + + } + + } + + // Else we only do the scaling operation in the scale table. + + else + { + + fScale_buffer.Reset (host.Allocate (0x10000 * 4)); + + for (j = 0; j < 0x10000; j++) + { + + uint32 x = j; + + // Apply linearization table, if any. + + if (lut) + { + + x = Min_uint32 (x, lutEntries - 1); + + x = lut [x]; + + } + + // Apply scale. + + real64 y = x * scale; + + // Store output value in table. + + if (fReal32) + { + + fScale_buffer->Buffer_real32 () [j] = (real32) y; + + } + + else + { + + int32 z = Round_int32 (y * 0x0FFFF * 256.0); + + fScale_buffer->Buffer_int32 () [j] = z; + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_linearize_plane::~dng_linearize_plane () + { + + } + +/*****************************************************************************/ + +void dng_linearize_plane::Process (const dng_rect &srcTile) + { + + // Process tile. + + dng_rect dstTile = srcTile - fActiveArea.TL (); + + dng_const_tile_buffer srcBuffer (fSrcImage, srcTile); + dng_dirty_tile_buffer dstBuffer (fDstImage, dstTile); + + int32 sStep = srcBuffer.fColStep; + int32 dStep = dstBuffer.fColStep; + + uint32 count = srcTile.W (); + + uint32 dstCol = dstTile.l; + + uint32 rows = srcTile.H (); + + for (uint32 row = 0; row < rows; row++) + { + + uint32 dstRow = dstTile.t + row; + + const void *sPtr = srcBuffer.ConstPixel (srcTile.t + row, + srcTile.l, + fPlane); + + void *dPtr = dstBuffer.DirtyPixel (dstRow, + dstCol, + fPlane); + + // Floating point source case. + + if (fSrcPixelType == ttFloat) + { + + real32 scale = fScale; + + const real32 *srcPtr = (const real32 *) sPtr; + + real32 *dstPtr = (real32 *) dPtr; + + // Optimize scale only case, which is the most common. + + if (fBlack_1D_rows == 0 && + fBlack_2D_cols == 0) + { + + for (uint32 j = 0; j < count; j++) + { + + *dstPtr = (*srcPtr) * scale; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + else + { + + real32 b1 = 0.0f; + + if (fBlack_1D_rows) + { + b1 = fBlack_1D_buffer->Buffer_real32 () [dstRow % fBlack_1D_rows]; + } + + const real32 *b2 = NULL; + + uint32 b2_count = fBlack_2D_cols; + uint32 b2_phase = 0; + + if (b2_count) + { + + b2 = fBlack_2D_buffer->Buffer_real32 () + + b2_count * (dstRow % fBlack_2D_rows); + + b2_phase = dstCol % b2_count; + + } + + for (uint32 j = 0; j < count; j++) + { + + real32 x = (*srcPtr) * scale - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + *dstPtr = x; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + } + + // Simple LUT case. + + else if (fBlack_1D_rows == 0 && + fBlack_2D_rows == 0 && fSrcPixelType != ttLong) + { + + if (fDstPixelType == ttShort) + { + + const uint16 *lut = fScale_buffer->Buffer_uint16 (); + + uint16 *dstPtr = (uint16 *) dPtr; + + if (fSrcPixelType == ttByte) + { + + const uint8 *srcPtr = (const uint8 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + *dstPtr = lut [*srcPtr]; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + else + { + + const uint16 *srcPtr = (const uint16 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + *dstPtr = lut [*srcPtr]; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + } + + else + { + + const real32 *lut = fScale_buffer->Buffer_real32 (); + + real32 *dstPtr = (real32 *) dPtr; + + if (fSrcPixelType == ttByte) + { + + const uint8 *srcPtr = (const uint8 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + *dstPtr = lut [*srcPtr]; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + else + { + + const uint16 *srcPtr = (const uint16 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + *dstPtr = lut [*srcPtr]; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + } + + } + + // Integer math case. + + else if (!fReal32) + { + + const int32 *lut = fScale_buffer->Buffer_int32 (); + + int32 b1 = 0; + + if (fBlack_1D_rows) + { + b1 = fBlack_1D_buffer->Buffer_int32 () [dstRow % fBlack_1D_rows]; + } + + const int32 *b2 = NULL; + + uint32 b2_count = fBlack_2D_cols; + uint32 b2_phase = 0; + + if (b2_count) + { + + b2 = fBlack_2D_buffer->Buffer_int32 () + + b2_count * (dstRow % fBlack_2D_rows); + + b2_phase = dstCol % b2_count; + + } + + uint16 *dstPtr = (uint16 *) dPtr; + + b1 -= 128; // Rounding for 8 bit shift + + if (fSrcPixelType == ttByte) + { + + const uint8 *srcPtr = (const uint8 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + int32 x = lut [*srcPtr] - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + x >>= 8; + + *dstPtr = Pin_uint16 (x); + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + else + { + + const uint16 *srcPtr = (const uint16 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + int32 x = lut [*srcPtr] - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + x >>= 8; + + *dstPtr = Pin_uint16 (x); + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + } + + // Floating point math cases. + + else + { + + real32 b1 = 0.0f; + + if (fBlack_1D_rows) + { + b1 = fBlack_1D_buffer->Buffer_real32 () [dstRow % fBlack_1D_rows]; + } + + const real32 *b2 = NULL; + + uint32 b2_count = fBlack_2D_cols; + uint32 b2_phase = 0; + + if (b2_count) + { + + b2 = fBlack_2D_buffer->Buffer_real32 () + + b2_count * (dstRow % fBlack_2D_rows); + + b2_phase = dstCol % b2_count; + + } + + // Case 1: uint8/uint16 -> real32 + + if (fSrcPixelType != ttLong) + { + + const real32 *lut = fScale_buffer->Buffer_real32 (); + + real32 *dstPtr = (real32 *) dPtr; + + if (fSrcPixelType == ttByte) + { + + const uint8 *srcPtr = (const uint8 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + real32 x = lut [*srcPtr] - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + x = Pin_real32 (0.0f, x, 1.0f); + + *dstPtr = x; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + else + { + + const uint16 *srcPtr = (const uint16 *) sPtr; + + for (uint32 j = 0; j < count; j++) + { + + real32 x = lut [*srcPtr] - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + x = Pin_real32 (0.0f, x, 1.0f); + + *dstPtr = x; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + } + + // Otherwise source is uint32 + + else + { + + real32 scale = fScale; + + const uint32 *srcPtr = (const uint32 *) sPtr; + + // Case 2: uint32 -> real32 + + if (fDstPixelType == ttFloat) + { + + real32 *dstPtr = (real32 *) dPtr; + + for (uint32 j = 0; j < count; j++) + { + + real32 x = ((real32) *srcPtr) * scale - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + x = Pin_real32 (0.0f, x, 1.0f); + + *dstPtr = x; + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + // Case 3: uint32 -> uint16 + + else + { + + uint16 *dstPtr = (uint16 *) dPtr; + + real32 dstScale = (real32) 0x0FFFF; + + for (uint32 j = 0; j < count; j++) + { + + real32 x = ((real32) *srcPtr) * scale - b1; + + if (b2_count) + { + + x -= b2 [b2_phase]; + + if (++b2_phase == b2_count) + { + b2_phase = 0; + } + + } + + x = Pin_real32 (0.0f, x, 1.0f); + + *dstPtr = (uint16) (x * dstScale + 0.5f); + + srcPtr += sStep; + dstPtr += dStep; + + } + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +class dng_linearize_image: public dng_area_task + { + + private: + + const dng_image & fSrcImage; + dng_image & fDstImage; + + dng_rect fActiveArea; + + AutoPtr fPlaneTask [kMaxColorPlanes]; + + public: + + dng_linearize_image (dng_host &host, + dng_linearization_info &info, + const dng_image &srcImage, + dng_image &dstImage); + + virtual ~dng_linearize_image (); + + virtual dng_rect RepeatingTile1 () const; + + virtual dng_rect RepeatingTile2 () const; + + virtual void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer *sniffer); + + }; + +/*****************************************************************************/ + +dng_linearize_image::dng_linearize_image (dng_host &host, + dng_linearization_info &info, + const dng_image &srcImage, + dng_image &dstImage) + + : fSrcImage (srcImage) + , fDstImage (dstImage) + , fActiveArea (info.fActiveArea) + + { + + // Build linearization table for each plane. + + for (uint32 plane = 0; plane < srcImage.Planes (); plane++) + { + + fPlaneTask [plane].Reset (new dng_linearize_plane (host, + info, + srcImage, + dstImage, + plane)); + + } + + // Adjust maximum tile size. + + fMaxTileSize = dng_point (1024, 1024); + + } + +/*****************************************************************************/ + +dng_linearize_image::~dng_linearize_image () + { + + } + +/*****************************************************************************/ + +dng_rect dng_linearize_image::RepeatingTile1 () const + { + + return fSrcImage.RepeatingTile (); + + } + +/*****************************************************************************/ + +dng_rect dng_linearize_image::RepeatingTile2 () const + { + + return fDstImage.RepeatingTile () + fActiveArea.TL (); + + } + +/*****************************************************************************/ + +void dng_linearize_image::Process (uint32 /* threadIndex */, + const dng_rect &srcTile, + dng_abort_sniffer * /* sniffer */) + { + + // Process each plane. + + for (uint32 plane = 0; plane < fSrcImage.Planes (); plane++) + { + + fPlaneTask [plane]->Process (srcTile); + + } + + } + +/*****************************************************************************/ + +dng_linearization_info::dng_linearization_info () + + : fActiveArea () + , fMaskedAreaCount (0) + , fLinearizationTable () + , fBlackLevelRepeatRows (1) + , fBlackLevelRepeatCols (1) + , fBlackDeltaH () + , fBlackDeltaV () + , fBlackDenom (256) + + { + + uint32 j; + uint32 k; + uint32 n; + + for (j = 0; j < kMaxBlackPattern; j++) + for (k = 0; k < kMaxBlackPattern; k++) + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + fBlackLevel [j] [k] [n] = 0.0; + } + + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + fWhiteLevel [n] = 65535.0; + } + + } + +/*****************************************************************************/ + +dng_linearization_info::~dng_linearization_info () + { + + } + +/*****************************************************************************/ + +void dng_linearization_info::RoundBlacks () + { + + uint32 j; + uint32 k; + uint32 n; + + real64 maxAbs = 0.0; + + for (j = 0; j < fBlackLevelRepeatRows; j++) + for (k = 0; k < fBlackLevelRepeatCols; k++) + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + + maxAbs = Max_real64 (maxAbs, + Abs_real64 (fBlackLevel [j] [k] [n])); + + } + + uint32 count = RowBlackCount (); + + for (j = 0; j < count; j++) + { + + maxAbs = Max_real64 (maxAbs, + Abs_real64 (fBlackDeltaV->Buffer_real64 () [j])); + + } + + count = ColumnBlackCount (); + + for (j = 0; j < count; j++) + { + + maxAbs = Max_real64 (maxAbs, + Abs_real64 (fBlackDeltaH->Buffer_real64 () [j])); + + + } + + fBlackDenom = 256; + + while (fBlackDenom > 1 && (maxAbs * fBlackDenom) >= 30000.0 * 65536.0) + { + fBlackDenom >>= 1; + } + + for (j = 0; j < fBlackLevelRepeatRows; j++) + for (k = 0; k < fBlackLevelRepeatCols; k++) + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + + fBlackLevel [j] [k] [n] = BlackLevel (j, k, n).As_real64 (); + + } + + count = RowBlackCount (); + + for (j = 0; j < count; j++) + { + + fBlackDeltaV->Buffer_real64 () [j] = RowBlack (j).As_real64 (); + + } + + count = ColumnBlackCount (); + + for (j = 0; j < count; j++) + { + + fBlackDeltaH->Buffer_real64 () [j] = ColumnBlack (j).As_real64 (); + + } + + } + +/*****************************************************************************/ + +void dng_linearization_info::Parse (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + uint32 j; + uint32 k; + uint32 n; + + // Find main image IFD. + + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex].Get (); + + // Copy active area. + + fActiveArea = rawIFD.fActiveArea; + + // Copy masked areas. + + fMaskedAreaCount = rawIFD.fMaskedAreaCount; + + for (j = 0; j < fMaskedAreaCount; j++) + { + fMaskedArea [j] = rawIFD.fMaskedArea [j]; + } + + // Read linearization LUT. + + if (rawIFD.fLinearizationTableCount) + { + + uint32 size = rawIFD.fLinearizationTableCount * (uint32) sizeof (uint16); + + fLinearizationTable.Reset (host.Allocate (size)); + + uint16 *table = fLinearizationTable->Buffer_uint16 (); + + stream.SetReadPosition (rawIFD.fLinearizationTableOffset); + + for (j = 0; j < rawIFD.fLinearizationTableCount; j++) + { + table [j] = stream.Get_uint16 (); + } + + } + + // Copy black level pattern. + + fBlackLevelRepeatRows = rawIFD.fBlackLevelRepeatRows; + fBlackLevelRepeatCols = rawIFD.fBlackLevelRepeatCols; + + for (j = 0; j < kMaxBlackPattern; j++) + for (k = 0; k < kMaxBlackPattern; k++) + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + fBlackLevel [j] [k] [n] = rawIFD.fBlackLevel [j] [k] [n]; + } + + // Read BlackDeltaH. + + if (rawIFD.fBlackLevelDeltaHCount) + { + + uint32 size = rawIFD.fBlackLevelDeltaHCount * (uint32) sizeof (real64); + + fBlackDeltaH.Reset (host.Allocate (size)); + + real64 *blacks = fBlackDeltaH->Buffer_real64 (); + + stream.SetReadPosition (rawIFD.fBlackLevelDeltaHOffset); + + for (j = 0; j < rawIFD.fBlackLevelDeltaHCount; j++) + { + blacks [j] = stream.TagValue_real64 (rawIFD.fBlackLevelDeltaHType); + } + + } + + // Read BlackDeltaV. + + if (rawIFD.fBlackLevelDeltaVCount) + { + + uint32 size = rawIFD.fBlackLevelDeltaVCount * (uint32) sizeof (real64); + + fBlackDeltaV.Reset (host.Allocate (size)); + + real64 *blacks = fBlackDeltaV->Buffer_real64 (); + + stream.SetReadPosition (rawIFD.fBlackLevelDeltaVOffset); + + for (j = 0; j < rawIFD.fBlackLevelDeltaVCount; j++) + { + blacks [j] = stream.TagValue_real64 (rawIFD.fBlackLevelDeltaVType); + } + + } + + // Copy white level. + + for (n = 0; n < kMaxSamplesPerPixel; n++) + { + fWhiteLevel [n] = rawIFD.fWhiteLevel [n]; + } + + // Round off black levels. + + RoundBlacks (); + + } + +/*****************************************************************************/ + +void dng_linearization_info::PostParse (dng_host & /* host */, + dng_negative &negative) + { + + if (fActiveArea.IsEmpty ()) + { + + fActiveArea = negative.Stage1Image ()->Bounds (); + + } + + } + +/*****************************************************************************/ + +real64 dng_linearization_info::MaxBlackLevel (uint32 plane) const + { + + uint32 j; + uint32 k; + + // Find maximum value of fBlackDeltaH for each phase of black pattern. + + real64 maxDeltaH [kMaxBlackPattern]; + + for (j = 0; j < fBlackLevelRepeatCols; j++) + { + maxDeltaH [j] = 0.0; + } + + if (fBlackDeltaH.Get ()) + { + + real64 *table = fBlackDeltaH->Buffer_real64 (); + + uint32 entries = fBlackDeltaH->LogicalSize () / (uint32) sizeof (table [0]); + + for (j = 0; j < entries; j++) + { + + real64 &entry = maxDeltaH [j % fBlackLevelRepeatCols]; + + if (j < fBlackLevelRepeatCols) + { + entry = table [j]; + } + else + { + entry = Max_real64 (entry, table [j]); + } + + } + + } + + // Find maximum value of fBlackDeltaV for each phase of black pattern. + + real64 maxDeltaV [kMaxBlackPattern]; + + for (j = 0; j < fBlackLevelRepeatRows; j++) + { + maxDeltaV [j] = 0.0; + } + + if (fBlackDeltaV.Get ()) + { + + real64 *table = fBlackDeltaV->Buffer_real64 (); + + uint32 entries = fBlackDeltaV->LogicalSize () / (uint32) sizeof (table [0]); + + for (j = 0; j < entries; j++) + { + + real64 &entry = maxDeltaV [j % fBlackLevelRepeatRows]; + + if (j < fBlackLevelRepeatRows) + { + entry = table [j]; + } + else + { + entry = Max_real64 (entry, table [j]); + } + + } + + } + + // Now scan the pattern and find the maximum value after row and column + // deltas. + + real64 maxBlack = 0.0; + + for (j = 0; j < fBlackLevelRepeatRows; j++) + { + + for (k = 0; k < fBlackLevelRepeatCols; k++) + { + + real64 black = fBlackLevel [j] [k] [plane]; + + black += maxDeltaH [k]; + black += maxDeltaV [j]; + + if (j == 0 && k == 0) + { + maxBlack = black; + } + else + { + maxBlack = Max_real64 (maxBlack, black); + } + + } + + } + + return maxBlack; + + } + +/*****************************************************************************/ + +void dng_linearization_info::Linearize (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage) + { + + dng_linearize_image processor (host, + *this, + srcImage, + dstImage); + + host.PerformAreaTask (processor, + fActiveArea); + + } + +/*****************************************************************************/ + +dng_urational dng_linearization_info::BlackLevel (uint32 row, + uint32 col, + uint32 plane) const + { + + dng_urational r; + + r.Set_real64 (fBlackLevel [row] [col] [plane], fBlackDenom); + + return r; + + } + +/*****************************************************************************/ + +uint32 dng_linearization_info::RowBlackCount () const + { + + if (fBlackDeltaV.Get ()) + { + + return fBlackDeltaV->LogicalSize () >> 3; + + } + + return 0; + + } + +/*****************************************************************************/ + +dng_srational dng_linearization_info::RowBlack (uint32 row) const + { + + if (fBlackDeltaV.Get ()) + { + + dng_srational r; + + r.Set_real64 (fBlackDeltaV->Buffer_real64 () [row], fBlackDenom); + + return r; + + } + + return dng_srational (0, 1); + + } + +/*****************************************************************************/ + +uint32 dng_linearization_info::ColumnBlackCount () const + { + + if (fBlackDeltaH.Get ()) + { + + return fBlackDeltaH->LogicalSize () >> 3; + + } + + return 0; + + } + +/*****************************************************************************/ + +dng_srational dng_linearization_info::ColumnBlack (uint32 col) const + { + + if (fBlackDeltaH.Get ()) + { + + dng_srational r; + + r.Set_real64 (fBlackDeltaH->Buffer_real64 () [col], fBlackDenom); + + return r; + + } + + return dng_srational (0, 1); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_linearization_info.h b/source/lib/dng_sdk/dng_linearization_info.h new file mode 100644 index 0000000..510cf11 --- /dev/null +++ b/source/lib/dng_sdk/dng_linearization_info.h @@ -0,0 +1,164 @@ +/*****************************************************************************/ +// Copyright 2006-2011 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_linearization_info.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Support for linearization table and black level tags. + */ + +/*****************************************************************************/ + +#ifndef __dng_linearization_info__ +#define __dng_linearization_info__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_memory.h" +#include "dng_rational.h" +#include "dng_rect.h" +#include "dng_sdk_limits.h" + +/*****************************************************************************/ + +/// \brief Class for managing data values related to DNG linearization. +/// +/// See LinearizationTable, BlackLevel, BlackLevelRepeatDim, BlackLevelDeltaH, +/// BlackLevelDeltaV and WhiteLevel tags in the \ref spec_dng "DNG 1.1.0 specification". + +class dng_linearization_info + { + + public: + + /// This rectangle defines the active (non-masked) pixels of the sensor. + /// The order of the rectangle coordinates is: top, left, bottom, right. + + dng_rect fActiveArea; + + /// Number of rectangles in fMaskedArea + + uint32 fMaskedAreaCount; + + /// List of non-overlapping rectangle coordinates of fully masked pixels. + /// Can be optionally used by DNG readers to measure the black encoding level. + /// The order of each rectangle's coordinates is: top, left, bottom, right. + /// If the raw image data has already had its black encoding level subtracted, then this tag should + /// not be used, since the masked pixels are no longer useful. + /// Note that DNG writers are still required to include an estimate and store the black encoding level + /// using the black level DNG tags. Support for the MaskedAreas tag is not required of DNG + /// readers. + + dng_rect fMaskedArea [kMaxMaskedAreas]; + + /// A lookup table that maps stored values into linear values. + /// This tag is typically used to increase compression ratios by storing the raw data in a non-linear, more + /// visually uniform space with fewer total encoding levels. + /// If SamplesPerPixel is not equal to one, e.g. Fuji S3 type sensor, this single table applies to all the samples for each + /// pixel. + + AutoPtr fLinearizationTable; + + /// Actual number of rows in fBlackLevel pattern + + uint32 fBlackLevelRepeatRows; + + /// Actual number of columns in fBlackLevel pattern + + uint32 fBlackLevelRepeatCols; + + /// Repeating pattern of black level deltas fBlackLevelRepeatRows by fBlackLevelRepeatCols in size. + + real64 fBlackLevel [kMaxBlackPattern] [kMaxBlackPattern] [kMaxSamplesPerPixel]; + + /// Memory block of double-precision floating point deltas between baseline black level and a given column's black level + + AutoPtr fBlackDeltaH; + + /// Memory block of double-precision floating point deltas between baseline black level and a given row's black level + + AutoPtr fBlackDeltaV; + + /// Single white level (maximum sensor value) for each sample plane. + + real64 fWhiteLevel [kMaxSamplesPerPixel]; + + protected: + + int32 fBlackDenom; + + public: + + dng_linearization_info (); + + virtual ~dng_linearization_info (); + + void RoundBlacks (); + + virtual void Parse (dng_host &host, + dng_stream &stream, + dng_info &info); + + virtual void PostParse (dng_host &host, + dng_negative &negative); + + /// Compute the maximum black level for a given sample plane taking into account base + /// black level, repeated black level patter, and row/column delta maps. + + real64 MaxBlackLevel (uint32 plane) const; + + /// Convert raw data from in-file format to a true linear image using linearization data from DNG. + /// \param host Used to allocate buffers, check for aborts, and post progress updates. + /// \param srcImage Input pre-linearization RAW samples. + /// \param dstImage Output linearized image. + + virtual void Linearize (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage); + + /// Compute black level for one coordinate and sample plane in the image. + /// \param row Row to compute black level for. + /// \param col Column to compute black level for. + /// \param plane Sample plane to compute black level for. + + dng_urational BlackLevel (uint32 row, + uint32 col, + uint32 plane) const; + + /// Number of per-row black level deltas in fBlackDeltaV. + + uint32 RowBlackCount () const; + + /// Lookup black level delta for a given row. + /// \param row Row to get black level for. + /// \retval black level for indicated row. + + dng_srational RowBlack (uint32 row) const; + + /// Number of per-column black level deltas in fBlackDeltaV. + + uint32 ColumnBlackCount () const; + + /// Lookup black level delta for a given column. + /// \param col Column to get black level for. + /// \retval black level for indicated column. + + dng_srational ColumnBlack (uint32 col) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_lossless_jpeg.cpp b/source/lib/dng_sdk/dng_lossless_jpeg.cpp new file mode 100644 index 0000000..2b52983 --- /dev/null +++ b/source/lib/dng_sdk/dng_lossless_jpeg.cpp @@ -0,0 +1,3765 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_lossless_jpeg.cpp#2 $ */ +/* $DateTime: 2012/06/01 07:28:57 $ */ +/* $Change: 832715 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +// Lossless JPEG code adapted from: + +/* Copyright (C) 1991, 1992, Thomas G. Lane. + * Part of the Independent JPEG Group's software. + * See the file Copyright for more details. + * + * Copyright (c) 1993 Brian C. Smith, The Regents of the University + * of California + * All rights reserved. + * + * Copyright (c) 1994 Kongji Huang and Brian C. Smith. + * Cornell University + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software and its + * documentation for any purpose, without fee, and without written agreement is + * hereby granted, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * IN NO EVENT SHALL CORNELL UNIVERSITY BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF CORNELL + * UNIVERSITY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * CORNELL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND CORNELL UNIVERSITY HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +/*****************************************************************************/ +#include "stdc_includes.h" + +#include "dng_lossless_jpeg.h" + +#include "dng_assertions.h" +#include "dng_exceptions.h" +#include "dng_memory.h" +#include "dng_stream.h" +#include "dng_tag_codes.h" + +/*****************************************************************************/ + +// This module contains routines that should be as fast as possible, even +// at the expense of slight code size increases. + +#include "dng_fast_module.h" + +/*****************************************************************************/ + +// The qSupportCanon_sRAW stuff not actually required for DNG support, but +// only included to allow this code to be used on Canon sRAW files. + +#ifndef qSupportCanon_sRAW +#define qSupportCanon_sRAW 1 +#endif + +// The qSupportHasselblad_3FR stuff not actually required for DNG support, but +// only included to allow this code to be used on Hasselblad 3FR files. + +#ifndef qSupportHasselblad_3FR +#define qSupportHasselblad_3FR 1 +#endif + +/*****************************************************************************/ + +/* + * One of the following structures is created for each huffman coding + * table. We use the same structure for encoding and decoding, so there + * may be some extra fields for encoding that aren't used in the decoding + * and vice-versa. + */ + +struct HuffmanTable + { + + /* + * These two fields directly represent the contents of a JPEG DHT + * marker + */ + uint8 bits[17]; + uint8 huffval[256]; + + /* + * The remaining fields are computed from the above to allow more + * efficient coding and decoding. These fields should be considered + * private to the Huffman compression & decompression modules. + */ + + uint16 mincode[17]; + int32 maxcode[18]; + int16 valptr[17]; + int32 numbits[256]; + int32 value[256]; + + uint16 ehufco[256]; + int8 ehufsi[256]; + + }; + +/*****************************************************************************/ + +// Computes the derived fields in the Huffman table structure. + +static void FixHuffTbl (HuffmanTable *htbl) + { + + int32 l; + int32 i; + + const uint32 bitMask [] = + { + 0xffffffff, 0x7fffffff, 0x3fffffff, 0x1fffffff, + 0x0fffffff, 0x07ffffff, 0x03ffffff, 0x01ffffff, + 0x00ffffff, 0x007fffff, 0x003fffff, 0x001fffff, + 0x000fffff, 0x0007ffff, 0x0003ffff, 0x0001ffff, + 0x0000ffff, 0x00007fff, 0x00003fff, 0x00001fff, + 0x00000fff, 0x000007ff, 0x000003ff, 0x000001ff, + 0x000000ff, 0x0000007f, 0x0000003f, 0x0000001f, + 0x0000000f, 0x00000007, 0x00000003, 0x00000001 + }; + + // Figure C.1: make table of Huffman code length for each symbol + // Note that this is in code-length order. + + int8 huffsize [257]; + + int32 p = 0; + + for (l = 1; l <= 16; l++) + { + + for (i = 1; i <= (int32) htbl->bits [l]; i++) + huffsize [p++] = (int8) l; + + } + + huffsize [p] = 0; + + int32 lastp = p; + + // Figure C.2: generate the codes themselves + // Note that this is in code-length order. + + uint16 huffcode [257]; + + uint16 code = 0; + + int32 si = huffsize [0]; + + p = 0; + + while (huffsize [p]) + { + + while (((int32) huffsize [p]) == si) + { + huffcode [p++] = code; + code++; + } + + code <<= 1; + + si++; + + } + + // Figure C.3: generate encoding tables + // These are code and size indexed by symbol value + // Set any codeless symbols to have code length 0; this allows + // EmitBits to detect any attempt to emit such symbols. + + memset (htbl->ehufsi, 0, sizeof (htbl->ehufsi)); + + for (p = 0; p < lastp; p++) + { + + htbl->ehufco [htbl->huffval [p]] = huffcode [p]; + htbl->ehufsi [htbl->huffval [p]] = huffsize [p]; + + } + + // Figure F.15: generate decoding tables + + p = 0; + + for (l = 1; l <= 16; l++) + { + + if (htbl->bits [l]) + { + + htbl->valptr [l] = (int16) p; + htbl->mincode [l] = huffcode [p]; + + p += htbl->bits [l]; + + htbl->maxcode [l] = huffcode [p - 1]; + + } + + else + { + htbl->maxcode [l] = -1; + } + + } + + // We put in this value to ensure HuffDecode terminates. + + htbl->maxcode[17] = 0xFFFFFL; + + // Build the numbits, value lookup tables. + // These table allow us to gather 8 bits from the bits stream, + // and immediately lookup the size and value of the huffman codes. + // If size is zero, it means that more than 8 bits are in the huffman + // code (this happens about 3-4% of the time). + + memset (htbl->numbits, 0, sizeof (htbl->numbits)); + + for (p = 0; p < lastp; p++) + { + + int32 size = huffsize [p]; + + if (size <= 8) + { + + int32 value = htbl->huffval [p]; + + code = huffcode [p]; + + int32 ll = code << (8 -size); + + int32 ul = (size < 8 ? ll | bitMask [24 + size] + : ll); + + for (i = ll; i <= ul; i++) + { + htbl->numbits [i] = size; + htbl->value [i] = value; + } + + } + + } + + } + +/*****************************************************************************/ + +/* + * The following structure stores basic information about one component. + */ + +struct JpegComponentInfo + { + + /* + * These values are fixed over the whole image. + * They are read from the SOF marker. + */ + int16 componentId; /* identifier for this component (0..255) */ + int16 componentIndex; /* its index in SOF or cPtr->compInfo[] */ + + /* + * Downsampling is not normally used in lossless JPEG, although + * it is permitted by the JPEG standard (DIS). We set all sampling + * factors to 1 in this program. + */ + int16 hSampFactor; /* horizontal sampling factor */ + int16 vSampFactor; /* vertical sampling factor */ + + /* + * Huffman table selector (0..3). The value may vary + * between scans. It is read from the SOS marker. + */ + int16 dcTblNo; + + }; + +/* + * One of the following structures is used to pass around the + * decompression information. + */ + +struct DecompressInfo + { + + /* + * Image width, height, and image data precision (bits/sample) + * These fields are set by ReadFileHeader or ReadScanHeader + */ + int32 imageWidth; + int32 imageHeight; + int32 dataPrecision; + + /* + * compInfo[i] describes component that appears i'th in SOF + * numComponents is the # of color components in JPEG image. + */ + JpegComponentInfo *compInfo; + int16 numComponents; + + /* + * *curCompInfo[i] describes component that appears i'th in SOS. + * compsInScan is the # of color components in current scan. + */ + JpegComponentInfo *curCompInfo[4]; + int16 compsInScan; + + /* + * MCUmembership[i] indexes the i'th component of MCU into the + * curCompInfo array. + */ + int16 MCUmembership[10]; + + /* + * ptrs to Huffman coding tables, or NULL if not defined + */ + HuffmanTable *dcHuffTblPtrs[4]; + + /* + * prediction selection value (PSV) and point transform parameter (Pt) + */ + int32 Ss; + int32 Pt; + + /* + * In lossless JPEG, restart interval shall be an integer + * multiple of the number of MCU in a MCU row. + */ + int32 restartInterval;/* MCUs per restart interval, 0 = no restart */ + int32 restartInRows; /*if > 0, MCU rows per restart interval; 0 = no restart*/ + + /* + * these fields are private data for the entropy decoder + */ + int32 restartRowsToGo; /* MCUs rows left in this restart interval */ + int16 nextRestartNum; /* # of next RSTn marker (0..7) */ + + }; + +/*****************************************************************************/ + +// An MCU (minimum coding unit) is an array of samples. + +typedef uint16 ComponentType; // the type of image components + +typedef ComponentType *MCU; // MCU - array of samples + +/*****************************************************************************/ + +class dng_lossless_decoder + { + + private: + + dng_stream *fStream; // Input data. + + dng_spooler *fSpooler; // Output data. + + bool fBug16; // Decode data with the "16-bit" bug. + + dng_memory_data huffmanBuffer [4]; + + dng_memory_data compInfoBuffer; + + DecompressInfo info; + + dng_memory_data mcuBuffer1; + dng_memory_data mcuBuffer2; + dng_memory_data mcuBuffer3; + dng_memory_data mcuBuffer4; + + MCU *mcuROW1; + MCU *mcuROW2; + + uint64 getBuffer; // current bit-extraction buffer + int32 bitsLeft; // # of unused bits in it + + #if qSupportHasselblad_3FR + bool fHasselblad3FR; + #endif + + public: + + dng_lossless_decoder (dng_stream *stream, + dng_spooler *spooler, + bool bug16); + + void StartRead (uint32 &imageWidth, + uint32 &imageHeight, + uint32 &imageChannels); + + void FinishRead (); + + private: + + uint8 GetJpegChar () + { + return fStream->Get_uint8 (); + } + + void UnGetJpegChar () + { + fStream->SetReadPosition (fStream->Position () - 1); + } + + uint16 Get2bytes (); + + void SkipVariable (); + + void GetDht (); + + void GetDri (); + + void GetApp0 (); + + void GetSof (int32 code); + + void GetSos (); + + void GetSoi (); + + int32 NextMarker (); + + JpegMarker ProcessTables (); + + void ReadFileHeader (); + + int32 ReadScanHeader (); + + void DecoderStructInit (); + + void HuffDecoderInit (); + + void ProcessRestart (); + + int32 QuickPredict (int32 col, + int32 curComp, + MCU *curRowBuf, + MCU *prevRowBuf); + + void FillBitBuffer (int32 nbits); + + int32 show_bits8 (); + + void flush_bits (int32 nbits); + + int32 get_bits (int32 nbits); + + int32 get_bit (); + + int32 HuffDecode (HuffmanTable *htbl); + + void HuffExtend (int32 &x, int32 s); + + void PmPutRow (MCU *buf, + int32 numComp, + int32 numCol, + int32 row); + + void DecodeFirstRow (MCU *curRowBuf); + + void DecodeImage (); + + // Hidden copy constructor and assignment operator. + + dng_lossless_decoder (const dng_lossless_decoder &decoder); + + dng_lossless_decoder & operator= (const dng_lossless_decoder &decoder); + + }; + +/*****************************************************************************/ + +dng_lossless_decoder::dng_lossless_decoder (dng_stream *stream, + dng_spooler *spooler, + bool bug16) + + : fStream (stream ) + , fSpooler (spooler) + , fBug16 (bug16 ) + + , compInfoBuffer () + , info () + , mcuBuffer1 () + , mcuBuffer2 () + , mcuBuffer3 () + , mcuBuffer4 () + , mcuROW1 (NULL) + , mcuROW2 (NULL) + , getBuffer (0) + , bitsLeft (0) + + #if qSupportHasselblad_3FR + , fHasselblad3FR (false) + #endif + + { + + memset (&info, 0, sizeof (info)); + + } + +/*****************************************************************************/ + +uint16 dng_lossless_decoder::Get2bytes () + { + + uint16 a = GetJpegChar (); + + return (uint16) ((a << 8) + GetJpegChar ()); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * SkipVariable -- + * + * Skip over an unknown or uninteresting variable-length marker + * + * Results: + * None. + * + * Side effects: + * Bitstream is parsed over marker. + * + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::SkipVariable () + { + + uint32 length = Get2bytes () - 2; + + fStream->Skip (length); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GetDht -- + * + * Process a DHT marker + * + * Results: + * None + * + * Side effects: + * A huffman table is read. + * Exits on error. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::GetDht () + { + + int32 length = Get2bytes () - 2; + + while (length > 0) + { + + int32 index = GetJpegChar (); + + if (index < 0 || index >= 4) + { + ThrowBadFormat (); + } + + HuffmanTable *&htblptr = info.dcHuffTblPtrs [index]; + + if (htblptr == NULL) + { + + huffmanBuffer [index] . Allocate (sizeof (HuffmanTable)); + + htblptr = (HuffmanTable *) huffmanBuffer [index] . Buffer (); + + } + + htblptr->bits [0] = 0; + + int32 count = 0; + + for (int32 i = 1; i <= 16; i++) + { + + htblptr->bits [i] = GetJpegChar (); + + count += htblptr->bits [i]; + + } + + if (count > 256) + { + ThrowBadFormat (); + } + + for (int32 j = 0; j < count; j++) + { + + htblptr->huffval [j] = GetJpegChar (); + + } + + length -= 1 + 16 + count; + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GetDri -- + * + * Process a DRI marker + * + * Results: + * None + * + * Side effects: + * Exits on error. + * Bitstream is parsed. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::GetDri () + { + + if (Get2bytes () != 4) + { + ThrowBadFormat (); + } + + info.restartInterval = Get2bytes (); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GetApp0 -- + * + * Process an APP0 marker. + * + * Results: + * None + * + * Side effects: + * Bitstream is parsed + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::GetApp0 () + { + + SkipVariable (); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GetSof -- + * + * Process a SOFn marker + * + * Results: + * None. + * + * Side effects: + * Bitstream is parsed + * Exits on error + * info structure is filled in + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::GetSof (int32 /*code*/) + { + + int32 length = Get2bytes (); + + info.dataPrecision = GetJpegChar (); + info.imageHeight = Get2bytes (); + info.imageWidth = Get2bytes (); + info.numComponents = GetJpegChar (); + + // We don't support files in which the image height is initially + // specified as 0 and is later redefined by DNL. As long as we + // have to check that, might as well have a general sanity check. + + if ((info.imageHeight <= 0) || + (info.imageWidth <= 0) || + (info.numComponents <= 0)) + { + ThrowBadFormat (); + } + + // Lossless JPEG specifies data precision to be from 2 to 16 bits/sample. + + const int32 MinPrecisionBits = 2; + const int32 MaxPrecisionBits = 16; + + if ((info.dataPrecision < MinPrecisionBits) || + (info.dataPrecision > MaxPrecisionBits)) + { + ThrowBadFormat (); + } + + // Check length of tag. + + if (length != (info.numComponents * 3 + 8)) + { + ThrowBadFormat (); + } + + // Allocate per component info. + + compInfoBuffer.Allocate (info.numComponents * + (uint32) sizeof (JpegComponentInfo)); + + info.compInfo = (JpegComponentInfo *) compInfoBuffer.Buffer (); + + // Read in the per compent info. + + for (int32 ci = 0; ci < info.numComponents; ci++) + { + + JpegComponentInfo *compptr = &info.compInfo [ci]; + + compptr->componentIndex = (int16) ci; + + compptr->componentId = GetJpegChar (); + + int32 c = GetJpegChar (); + + compptr->hSampFactor = (int16) ((c >> 4) & 15); + compptr->vSampFactor = (int16) ((c ) & 15); + + (void) GetJpegChar (); /* skip Tq */ + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GetSos -- + * + * Process a SOS marker + * + * Results: + * None. + * + * Side effects: + * Bitstream is parsed. + * Exits on error. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::GetSos () + { + + int32 length = Get2bytes (); + + // Get the number of image components. + + int32 n = GetJpegChar (); + info.compsInScan = (int16) n; + + // Check length. + + length -= 3; + + if (length != (n * 2 + 3) || n < 1 || n > 4) + { + ThrowBadFormat (); + } + + // Find index and huffman table for each component. + + for (int32 i = 0; i < n; i++) + { + + int32 cc = GetJpegChar (); + int32 c = GetJpegChar (); + + int32 ci; + + for (ci = 0; ci < info.numComponents; ci++) + { + + if (cc == info.compInfo[ci].componentId) + { + break; + } + + } + + if (ci >= info.numComponents) + { + ThrowBadFormat (); + } + + JpegComponentInfo *compptr = &info.compInfo [ci]; + + info.curCompInfo [i] = compptr; + + compptr->dcTblNo = (int16) ((c >> 4) & 15); + + } + + // Get the PSV, skip Se, and get the point transform parameter. + + info.Ss = GetJpegChar (); + + (void) GetJpegChar (); + + info.Pt = GetJpegChar () & 0x0F; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GetSoi -- + * + * Process an SOI marker + * + * Results: + * None. + * + * Side effects: + * Bitstream is parsed. + * Exits on error. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::GetSoi () + { + + // Reset all parameters that are defined to be reset by SOI + + info.restartInterval = 0; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * NextMarker -- + * + * Find the next JPEG marker Note that the output might not + * be a valid marker code but it will never be 0 or FF + * + * Results: + * The marker found. + * + * Side effects: + * Bitstream is parsed. + * + *-------------------------------------------------------------- + */ + +int32 dng_lossless_decoder::NextMarker () + { + + int32 c; + + do + { + + // skip any non-FF bytes + + do + { + c = GetJpegChar (); + } + while (c != 0xFF); + + // skip any duplicate FFs, since extra FFs are legal + + do + { + c = GetJpegChar(); + } + while (c == 0xFF); + + } + while (c == 0); // repeat if it was a stuffed FF/00 + + return c; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * ProcessTables -- + * + * Scan and process JPEG markers that can appear in any order + * Return when an SOI, EOI, SOFn, or SOS is found + * + * Results: + * The marker found. + * + * Side effects: + * Bitstream is parsed. + * + *-------------------------------------------------------------- + */ + +JpegMarker dng_lossless_decoder::ProcessTables () + { + + while (true) + { + + int32 c = NextMarker (); + + switch (c) + { + + case M_SOF0: + case M_SOF1: + case M_SOF2: + case M_SOF3: + case M_SOF5: + case M_SOF6: + case M_SOF7: + case M_JPG: + case M_SOF9: + case M_SOF10: + case M_SOF11: + case M_SOF13: + case M_SOF14: + case M_SOF15: + case M_SOI: + case M_EOI: + case M_SOS: + return (JpegMarker) c; + + case M_DHT: + GetDht (); + break; + + case M_DQT: + break; + + case M_DRI: + GetDri (); + break; + + case M_APP0: + GetApp0 (); + break; + + case M_RST0: // these are all parameterless + case M_RST1: + case M_RST2: + case M_RST3: + case M_RST4: + case M_RST5: + case M_RST6: + case M_RST7: + case M_TEM: + break; + + default: // must be DNL, DHP, EXP, APPn, JPGn, COM, or RESn + SkipVariable (); + break; + + } + + } + + return M_ERROR; + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * ReadFileHeader -- + * + * Initialize and read the stream header (everything through + * the SOF marker). + * + * Results: + * None + * + * Side effects: + * Exit on error. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::ReadFileHeader () + { + + // Demand an SOI marker at the start of the stream --- otherwise it's + // probably not a JPEG stream at all. + + int32 c = GetJpegChar (); + int32 c2 = GetJpegChar (); + + if ((c != 0xFF) || (c2 != M_SOI)) + { + ThrowBadFormat (); + } + + // OK, process SOI + + GetSoi (); + + // Process markers until SOF + + c = ProcessTables (); + + switch (c) + { + + case M_SOF0: + case M_SOF1: + case M_SOF3: + GetSof (c); + break; + + default: + ThrowBadFormat (); + break; + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * ReadScanHeader -- + * + * Read the start of a scan (everything through the SOS marker). + * + * Results: + * 1 if find SOS, 0 if find EOI + * + * Side effects: + * Bitstream is parsed, may exit on errors. + * + *-------------------------------------------------------------- + */ + +int32 dng_lossless_decoder::ReadScanHeader () + { + + // Process markers until SOS or EOI + + int32 c = ProcessTables (); + + switch (c) + { + + case M_SOS: + GetSos (); + return 1; + + case M_EOI: + return 0; + + default: + ThrowBadFormat (); + break; + + } + + return 0; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * DecoderStructInit -- + * + * Initalize the rest of the fields in the decompression + * structure. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::DecoderStructInit () + { + + int32 ci; + + #if qSupportCanon_sRAW + + bool canon_sRAW = (info.numComponents == 3) && + (info.compInfo [0].hSampFactor == 2) && + (info.compInfo [1].hSampFactor == 1) && + (info.compInfo [2].hSampFactor == 1) && + (info.compInfo [0].vSampFactor == 1) && + (info.compInfo [1].vSampFactor == 1) && + (info.compInfo [2].vSampFactor == 1) && + (info.dataPrecision == 15) && + (info.Ss == 1) && + ((info.imageWidth & 1) == 0); + + bool canon_sRAW2 = (info.numComponents == 3) && + (info.compInfo [0].hSampFactor == 2) && + (info.compInfo [1].hSampFactor == 1) && + (info.compInfo [2].hSampFactor == 1) && + (info.compInfo [0].vSampFactor == 2) && + (info.compInfo [1].vSampFactor == 1) && + (info.compInfo [2].vSampFactor == 1) && + (info.dataPrecision == 15) && + (info.Ss == 1) && + ((info.imageWidth & 1) == 0) && + ((info.imageHeight & 1) == 0); + + if (!canon_sRAW && !canon_sRAW2) + + #endif + + { + + // Check sampling factor validity. + + for (ci = 0; ci < info.numComponents; ci++) + { + + JpegComponentInfo *compPtr = &info.compInfo [ci]; + + if (compPtr->hSampFactor != 1 || + compPtr->vSampFactor != 1) + { + ThrowBadFormat (); + } + + } + + } + + // Prepare array describing MCU composition. + + if (info.compsInScan > 4) + { + ThrowBadFormat (); + } + + for (ci = 0; ci < info.compsInScan; ci++) + { + info.MCUmembership [ci] = (int16) ci; + } + + // Initialize mucROW1 and mcuROW2 which buffer two rows of + // pixels for predictor calculation. + + int32 mcuSize = info.compsInScan * (uint32) sizeof (ComponentType); + + mcuBuffer1.Allocate (info.imageWidth * (uint32) sizeof (MCU)); + mcuBuffer2.Allocate (info.imageWidth * (uint32) sizeof (MCU)); + + mcuROW1 = (MCU *) mcuBuffer1.Buffer (); + mcuROW2 = (MCU *) mcuBuffer2.Buffer (); + + mcuBuffer3.Allocate (info.imageWidth * mcuSize); + mcuBuffer4.Allocate (info.imageWidth * mcuSize); + + mcuROW1 [0] = (ComponentType *) mcuBuffer3.Buffer (); + mcuROW2 [0] = (ComponentType *) mcuBuffer4.Buffer (); + + for (int32 j = 1; j < info.imageWidth; j++) + { + + mcuROW1 [j] = mcuROW1 [j - 1] + info.compsInScan; + mcuROW2 [j] = mcuROW2 [j - 1] + info.compsInScan; + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * HuffDecoderInit -- + * + * Initialize for a Huffman-compressed scan. + * This is invoked after reading the SOS marker. + * + * Results: + * None + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::HuffDecoderInit () + { + + // Initialize bit parser state + + getBuffer = 0; + bitsLeft = 0; + + // Prepare Huffman tables. + + for (int16 ci = 0; ci < info.compsInScan; ci++) + { + + JpegComponentInfo *compptr = info.curCompInfo [ci]; + + // Make sure requested tables are present + + if (compptr->dcTblNo < 0 || compptr->dcTblNo > 3) + { + ThrowBadFormat (); + } + + if (info.dcHuffTblPtrs [compptr->dcTblNo] == NULL) + { + ThrowBadFormat (); + } + + // Compute derived values for Huffman tables. + // We may do this more than once for same table, but it's not a + // big deal + + FixHuffTbl (info.dcHuffTblPtrs [compptr->dcTblNo]); + + } + + // Initialize restart stuff + + info.restartInRows = info.restartInterval / info.imageWidth; + info.restartRowsToGo = info.restartInRows; + info.nextRestartNum = 0; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * ProcessRestart -- + * + * Check for a restart marker & resynchronize decoder. + * + * Results: + * None. + * + * Side effects: + * BitStream is parsed, bit buffer is reset, etc. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::ProcessRestart () + { + + // Throw away and unused odd bits in the bit buffer. + + fStream->SetReadPosition (fStream->Position () - bitsLeft / 8); + + bitsLeft = 0; + getBuffer = 0; + + // Scan for next JPEG marker + + int32 c; + + do + { + + // skip any non-FF bytes + + do + { + c = GetJpegChar (); + } + while (c != 0xFF); + + // skip any duplicate FFs + + do + { + c = GetJpegChar (); + } + while (c == 0xFF); + + } + while (c == 0); // repeat if it was a stuffed FF/00 + + // Verify correct restart code. + + if (c != (M_RST0 + info.nextRestartNum)) + { + ThrowBadFormat (); + } + + // Update restart state. + + info.restartRowsToGo = info.restartInRows; + info.nextRestartNum = (info.nextRestartNum + 1) & 7; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * QuickPredict -- + * + * Calculate the predictor for sample curRowBuf[col][curComp]. + * It does not handle the special cases at image edges, such + * as first row and first column of a scan. We put the special + * case checkings outside so that the computations in main + * loop can be simpler. This has enhenced the performance + * significantly. + * + * Results: + * predictor is passed out. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +inline int32 dng_lossless_decoder::QuickPredict (int32 col, + int32 curComp, + MCU *curRowBuf, + MCU *prevRowBuf) + { + + int32 diag = prevRowBuf [col - 1] [curComp]; + int32 upper = prevRowBuf [col ] [curComp]; + int32 left = curRowBuf [col - 1] [curComp]; + + switch (info.Ss) + { + + case 0: + return 0; + + case 1: + return left; + + case 2: + return upper; + + case 3: + return diag; + + case 4: + return left + upper - diag; + + case 5: + return left + ((upper - diag) >> 1); + + case 6: + return upper + ((left - diag) >> 1); + + case 7: + return (left + upper) >> 1; + + default: + { + ThrowBadFormat (); + return 0; + } + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * FillBitBuffer -- + * + * Load up the bit buffer with at least nbits + * Process any stuffed bytes at this time. + * + * Results: + * None + * + * Side effects: + * The bitwise global variables are updated. + * + *-------------------------------------------------------------- + */ + +inline void dng_lossless_decoder::FillBitBuffer (int32 nbits) + { + + const int32 kMinGetBits = sizeof (uint32) * 8 - 7; + + #if qSupportHasselblad_3FR + + if (fHasselblad3FR) + { + + while (bitsLeft < kMinGetBits) + { + + int32 c0 = GetJpegChar (); + int32 c1 = GetJpegChar (); + int32 c2 = GetJpegChar (); + int32 c3 = GetJpegChar (); + + getBuffer = (getBuffer << 8) | c3; + getBuffer = (getBuffer << 8) | c2; + getBuffer = (getBuffer << 8) | c1; + getBuffer = (getBuffer << 8) | c0; + + bitsLeft += 32; + + } + + return; + + } + + #endif + + while (bitsLeft < kMinGetBits) + { + + int32 c = GetJpegChar (); + + // If it's 0xFF, check and discard stuffed zero byte + + if (c == 0xFF) + { + + int32 c2 = GetJpegChar (); + + if (c2 != 0) + { + + // Oops, it's actually a marker indicating end of + // compressed data. Better put it back for use later. + + UnGetJpegChar (); + UnGetJpegChar (); + + // There should be enough bits still left in the data + // segment; if so, just break out of the while loop. + + if (bitsLeft >= nbits) + break; + + // Uh-oh. Corrupted data: stuff zeroes into the data + // stream, since this sometimes occurs when we are on the + // last show_bits8 during decoding of the Huffman + // segment. + + c = 0; + + } + + } + + getBuffer = (getBuffer << 8) | c; + + bitsLeft += 8; + + } + + } + +/*****************************************************************************/ + +inline int32 dng_lossless_decoder::show_bits8 () + { + + if (bitsLeft < 8) + FillBitBuffer (8); + + return (int32) ((getBuffer >> (bitsLeft - 8)) & 0xff); + + } + +/*****************************************************************************/ + +inline void dng_lossless_decoder::flush_bits (int32 nbits) + { + + bitsLeft -= nbits; + + } + +/*****************************************************************************/ + +inline int32 dng_lossless_decoder::get_bits (int32 nbits) + { + + if (bitsLeft < nbits) + FillBitBuffer (nbits); + + return (int32) ((getBuffer >> (bitsLeft -= nbits)) & (0x0FFFF >> (16 - nbits))); + + } + +/*****************************************************************************/ + +inline int32 dng_lossless_decoder::get_bit () + { + + if (!bitsLeft) + FillBitBuffer (1); + + return (int32) ((getBuffer >> (--bitsLeft)) & 1); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * HuffDecode -- + * + * Taken from Figure F.16: extract next coded symbol from + * input stream. This should becode a macro. + * + * Results: + * Next coded symbol + * + * Side effects: + * Bitstream is parsed. + * + *-------------------------------------------------------------- + */ + +inline int32 dng_lossless_decoder::HuffDecode (HuffmanTable *htbl) + { + + // If the huffman code is less than 8 bits, we can use the fast + // table lookup to get its value. It's more than 8 bits about + // 3-4% of the time. + + int32 code = show_bits8 (); + + if (htbl->numbits [code]) + { + + flush_bits (htbl->numbits [code]); + + return htbl->value [code]; + + } + + else + { + + flush_bits (8); + + int32 l = 8; + + while (code > htbl->maxcode [l]) + { + code = (code << 1) | get_bit (); + l++; + } + + // With garbage input we may reach the sentinel value l = 17. + + if (l > 16) + { + return 0; // fake a zero as the safest result + } + else + { + return htbl->huffval [htbl->valptr [l] + + ((int32) (code - htbl->mincode [l]))]; + } + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * HuffExtend -- + * + * Code and table for Figure F.12: extend sign bit + * + * Results: + * The extended value. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +inline void dng_lossless_decoder::HuffExtend (int32 &x, int32 s) + { + + if (x < (0x08000 >> (16 - s))) + { + x += (-1 << s) + 1; + } + + } + +/*****************************************************************************/ + +// Called from DecodeImage () to write one row. + +void dng_lossless_decoder::PmPutRow (MCU *buf, + int32 numComp, + int32 numCol, + int32 /* row */) + { + + uint16 *sPtr = &buf [0] [0]; + + uint32 pixels = numCol * numComp; + + fSpooler->Spool (sPtr, pixels * (uint32) sizeof (uint16)); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * DecodeFirstRow -- + * + * Decode the first raster line of samples at the start of + * the scan and at the beginning of each restart interval. + * This includes modifying the component value so the real + * value, not the difference is returned. + * + * Results: + * None. + * + * Side effects: + * Bitstream is parsed. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::DecodeFirstRow (MCU *curRowBuf) + { + + int32 compsInScan = info.compsInScan; + + // Process the first column in the row. + + for (int32 curComp = 0; curComp < compsInScan; curComp++) + { + + int32 ci = info.MCUmembership [curComp]; + + JpegComponentInfo *compptr = info.curCompInfo [ci]; + + HuffmanTable *dctbl = info.dcHuffTblPtrs [compptr->dcTblNo]; + + // Section F.2.2.1: decode the difference + + int32 d = 0; + + int32 s = HuffDecode (dctbl); + + if (s) + { + + if (s == 16 && !fBug16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + // Add the predictor to the difference. + + int32 Pr = info.dataPrecision; + int32 Pt = info.Pt; + + curRowBuf [0] [curComp] = (ComponentType) (d + (1 << (Pr-Pt-1))); + + } + + // Process the rest of the row. + + int32 numCOL = info.imageWidth; + + for (int32 col = 1; col < numCOL; col++) + { + + for (int32 curComp = 0; curComp < compsInScan; curComp++) + { + + int32 ci = info.MCUmembership [curComp]; + + JpegComponentInfo *compptr = info.curCompInfo [ci]; + + HuffmanTable *dctbl = info.dcHuffTblPtrs [compptr->dcTblNo]; + + // Section F.2.2.1: decode the difference + + int32 d = 0; + + int32 s = HuffDecode (dctbl); + + if (s) + { + + if (s == 16 && !fBug16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + // Add the predictor to the difference. + + curRowBuf [col] [curComp] = (ComponentType) (d + curRowBuf [col-1] [curComp]); + + } + + } + + // Update the restart counter + + if (info.restartInRows) + { + info.restartRowsToGo--; + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * DecodeImage -- + * + * Decode the input stream. This includes modifying + * the component value so the real value, not the + * difference is returned. + * + * Results: + * None. + * + * Side effects: + * Bitstream is parsed. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_decoder::DecodeImage () + { + + #define swap(type,a,b) {type c; c=(a); (a)=(b); (b)=c;} + + int32 numCOL = info.imageWidth; + int32 numROW = info.imageHeight; + int32 compsInScan = info.compsInScan; + + // Precompute the decoding table for each table. + + HuffmanTable *ht [4]; + + for (int32 curComp = 0; curComp < compsInScan; curComp++) + { + + int32 ci = info.MCUmembership [curComp]; + + JpegComponentInfo *compptr = info.curCompInfo [ci]; + + ht [curComp] = info.dcHuffTblPtrs [compptr->dcTblNo]; + + } + + MCU *prevRowBuf = mcuROW1; + MCU *curRowBuf = mcuROW2; + + #if qSupportCanon_sRAW + + // Canon sRAW support + + if (info.compInfo [0].hSampFactor == 2 && + info.compInfo [0].vSampFactor == 1) + { + + for (int32 row = 0; row < numROW; row++) + { + + // Initialize predictors. + + int32 p0; + int32 p1; + int32 p2; + + if (row == 0) + { + p0 = 1 << 14; + p1 = 1 << 14; + p2 = 1 << 14; + } + + else + { + p0 = prevRowBuf [0] [0]; + p1 = prevRowBuf [0] [1]; + p2 = prevRowBuf [0] [2]; + } + + for (int32 col = 0; col < numCOL; col += 2) + { + + // Read first luminance component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p0 += d; + + curRowBuf [col] [0] = (ComponentType) p0; + + } + + // Read second luminance component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p0 += d; + + curRowBuf [col + 1] [0] = (ComponentType) p0; + + } + + // Read first chroma component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [1]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p1 += d; + + curRowBuf [col ] [1] = (ComponentType) p1; + curRowBuf [col + 1] [1] = (ComponentType) p1; + + } + + // Read second chroma component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [2]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p2 += d; + + curRowBuf [col ] [2] = (ComponentType) p2; + curRowBuf [col + 1] [2] = (ComponentType) p2; + + } + + } + + PmPutRow (curRowBuf, compsInScan, numCOL, row); + + swap (MCU *, prevRowBuf, curRowBuf); + + } + + return; + + } + + if (info.compInfo [0].hSampFactor == 2 && + info.compInfo [0].vSampFactor == 2) + { + + for (int32 row = 0; row < numROW; row += 2) + { + + // Initialize predictors. + + int32 p0; + int32 p1; + int32 p2; + + if (row == 0) + { + p0 = 1 << 14; + p1 = 1 << 14; + p2 = 1 << 14; + } + + else + { + p0 = prevRowBuf [0] [0]; + p1 = prevRowBuf [0] [1]; + p2 = prevRowBuf [0] [2]; + } + + for (int32 col = 0; col < numCOL; col += 2) + { + + // Read first luminance component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p0 += d; + + prevRowBuf [col] [0] = (ComponentType) p0; + + } + + // Read second luminance component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p0 += d; + + prevRowBuf [col + 1] [0] = (ComponentType) p0; + + } + + // Read third luminance component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p0 += d; + + curRowBuf [col] [0] = (ComponentType) p0; + + } + + // Read fourth luminance component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p0 += d; + + curRowBuf [col + 1] [0] = (ComponentType) p0; + + } + + // Read first chroma component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [1]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p1 += d; + + prevRowBuf [col ] [1] = (ComponentType) p1; + prevRowBuf [col + 1] [1] = (ComponentType) p1; + + curRowBuf [col ] [1] = (ComponentType) p1; + curRowBuf [col + 1] [1] = (ComponentType) p1; + + } + + // Read second chroma component. + + { + + int32 d = 0; + + int32 s = HuffDecode (ht [2]); + + if (s) + { + + if (s == 16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + p2 += d; + + prevRowBuf [col ] [2] = (ComponentType) p2; + prevRowBuf [col + 1] [2] = (ComponentType) p2; + + curRowBuf [col ] [2] = (ComponentType) p2; + curRowBuf [col + 1] [2] = (ComponentType) p2; + + } + + } + + PmPutRow (prevRowBuf, compsInScan, numCOL, row); + PmPutRow (curRowBuf, compsInScan, numCOL, row); + + } + + return; + + } + + #endif + + #if qSupportHasselblad_3FR + + if (info.Ss == 8) + { + + fHasselblad3FR = true; + + for (int32 row = 0; row < numROW; row++) + { + + int32 p0 = 32768; + int32 p1 = 32768; + + for (int32 col = 0; col < numCOL; col += 2) + { + + int32 s0 = HuffDecode (ht [0]); + int32 s1 = HuffDecode (ht [0]); + + if (s0) + { + int32 d = get_bits (s0); + if (s0 == 16) + { + d = -32768; + } + else + { + HuffExtend (d, s0); + } + p0 += d; + } + + if (s1) + { + int32 d = get_bits (s1); + if (s1 == 16) + { + d = -32768; + } + else + { + HuffExtend (d, s1); + } + p1 += d; + } + + curRowBuf [col ] [0] = (ComponentType) p0; + curRowBuf [col + 1] [0] = (ComponentType) p1; + + } + + PmPutRow (curRowBuf, compsInScan, numCOL, row); + + } + + return; + + } + + #endif + + // Decode the first row of image. Output the row and + // turn this row into a previous row for later predictor + // calculation. + + DecodeFirstRow (mcuROW1); + + PmPutRow (mcuROW1, compsInScan, numCOL, 0); + + // Process each row. + + for (int32 row = 1; row < numROW; row++) + { + + // Account for restart interval, process restart marker if needed. + + if (info.restartInRows) + { + + if (info.restartRowsToGo == 0) + { + + ProcessRestart (); + + // Reset predictors at restart. + + DecodeFirstRow (curRowBuf); + + PmPutRow (curRowBuf, compsInScan, numCOL, row); + + swap (MCU *, prevRowBuf, curRowBuf); + + continue; + + } + + info.restartRowsToGo--; + + } + + // The upper neighbors are predictors for the first column. + + for (int32 curComp = 0; curComp < compsInScan; curComp++) + { + + // Section F.2.2.1: decode the difference + + int32 d = 0; + + int32 s = HuffDecode (ht [curComp]); + + if (s) + { + + if (s == 16 && !fBug16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + // First column of row above is predictor for first column. + + curRowBuf [0] [curComp] = (ComponentType) (d + prevRowBuf [0] [curComp]); + + } + + // For the rest of the column on this row, predictor + // calculations are based on PSV. + + if (compsInScan == 2 && info.Ss == 1) + { + + // This is the combination used by both the Canon and Kodak raw formats. + // Unrolling the general case logic results in a significant speed increase. + + uint16 *dPtr = &curRowBuf [1] [0]; + + int32 prev0 = dPtr [-2]; + int32 prev1 = dPtr [-1]; + + for (int32 col = 1; col < numCOL; col++) + { + + int32 s = HuffDecode (ht [0]); + + if (s) + { + + int32 d; + + if (s == 16 && !fBug16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + prev0 += d; + + } + + s = HuffDecode (ht [1]); + + if (s) + { + + int32 d; + + if (s == 16 && !fBug16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + prev1 += d; + + } + + dPtr [0] = (uint16) prev0; + dPtr [1] = (uint16) prev1; + + dPtr += 2; + + } + + } + + else + { + + for (int32 col = 1; col < numCOL; col++) + { + + for (int32 curComp = 0; curComp < compsInScan; curComp++) + { + + // Section F.2.2.1: decode the difference + + int32 d = 0; + + int32 s = HuffDecode (ht [curComp]); + + if (s) + { + + if (s == 16 && !fBug16) + { + d = -32768; + } + + else + { + d = get_bits (s); + HuffExtend (d, s); + } + + } + + // Predict the pixel value. + + int32 predictor = QuickPredict (col, + curComp, + curRowBuf, + prevRowBuf); + + // Save the difference. + + curRowBuf [col] [curComp] = (ComponentType) (d + predictor); + + } + + } + + } + + PmPutRow (curRowBuf, compsInScan, numCOL, row); + + swap (MCU *, prevRowBuf, curRowBuf); + + } + + #undef swap + + } + +/*****************************************************************************/ + +void dng_lossless_decoder::StartRead (uint32 &imageWidth, + uint32 &imageHeight, + uint32 &imageChannels) + { + + ReadFileHeader (); + ReadScanHeader (); + DecoderStructInit (); + HuffDecoderInit (); + + imageWidth = info.imageWidth; + imageHeight = info.imageHeight; + imageChannels = info.compsInScan; + + } + +/*****************************************************************************/ + +void dng_lossless_decoder::FinishRead () + { + + DecodeImage (); + + } + +/*****************************************************************************/ + +void DecodeLosslessJPEG (dng_stream &stream, + dng_spooler &spooler, + uint32 minDecodedSize, + uint32 maxDecodedSize, + bool bug16) + { + + dng_lossless_decoder decoder (&stream, + &spooler, + bug16); + + uint32 imageWidth; + uint32 imageHeight; + uint32 imageChannels; + + decoder.StartRead (imageWidth, + imageHeight, + imageChannels); + + uint32 decodedSize = imageWidth * + imageHeight * + imageChannels * + (uint32) sizeof (uint16); + + if (decodedSize < minDecodedSize || + decodedSize > maxDecodedSize) + { + ThrowBadFormat (); + } + + decoder.FinishRead (); + + } + +/*****************************************************************************/ + +class dng_lossless_encoder + { + + private: + + const uint16 *fSrcData; + + uint32 fSrcRows; + uint32 fSrcCols; + uint32 fSrcChannels; + uint32 fSrcBitDepth; + + int32 fSrcRowStep; + int32 fSrcColStep; + + dng_stream &fStream; + + HuffmanTable huffTable [4]; + + uint32 freqCount [4] [257]; + + // Current bit-accumulation buffer + + int32 huffPutBuffer; + int32 huffPutBits; + + // Lookup table for number of bits in an 8 bit value. + + int numBitsTable [256]; + + public: + + dng_lossless_encoder (const uint16 *srcData, + uint32 srcRows, + uint32 srcCols, + uint32 srcChannels, + uint32 srcBitDepth, + int32 srcRowStep, + int32 srcColStep, + dng_stream &stream); + + void Encode (); + + private: + + void EmitByte (uint8 value); + + void EmitBits (int code, int size); + + void FlushBits (); + + void CountOneDiff (int diff, uint32 *countTable); + + void EncodeOneDiff (int diff, HuffmanTable *dctbl); + + void FreqCountSet (); + + void HuffEncode (); + + void GenHuffCoding (HuffmanTable *htbl, uint32 *freq); + + void HuffOptimize (); + + void EmitMarker (JpegMarker mark); + + void Emit2bytes (int value); + + void EmitDht (int index); + + void EmitSof (JpegMarker code); + + void EmitSos (); + + void WriteFileHeader (); + + void WriteScanHeader (); + + void WriteFileTrailer (); + + }; + +/*****************************************************************************/ + +dng_lossless_encoder::dng_lossless_encoder (const uint16 *srcData, + uint32 srcRows, + uint32 srcCols, + uint32 srcChannels, + uint32 srcBitDepth, + int32 srcRowStep, + int32 srcColStep, + dng_stream &stream) + + : fSrcData (srcData ) + , fSrcRows (srcRows ) + , fSrcCols (srcCols ) + , fSrcChannels (srcChannels) + , fSrcBitDepth (srcBitDepth) + , fSrcRowStep (srcRowStep ) + , fSrcColStep (srcColStep ) + , fStream (stream ) + + , huffPutBuffer (0) + , huffPutBits (0) + + { + + // Initialize number of bits lookup table. + + numBitsTable [0] = 0; + + for (int i = 1; i < 256; i++) + { + + int temp = i; + int nbits = 1; + + while (temp >>= 1) + { + nbits++; + } + + numBitsTable [i] = nbits; + + } + + } + +/*****************************************************************************/ + +inline void dng_lossless_encoder::EmitByte (uint8 value) + { + + fStream.Put_uint8 (value); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * EmitBits -- + * + * Code for outputting bits to the file + * + * Only the right 24 bits of huffPutBuffer are used; the valid + * bits are left-justified in this part. At most 16 bits can be + * passed to EmitBits in one call, and we never retain more than 7 + * bits in huffPutBuffer between calls, so 24 bits are + * sufficient. + * + * Results: + * None. + * + * Side effects: + * huffPutBuffer and huffPutBits are updated. + * + *-------------------------------------------------------------- + */ + +inline void dng_lossless_encoder::EmitBits (int code, int size) + { + + DNG_ASSERT (size != 0, "Bad Huffman table entry"); + + int putBits = size; + int putBuffer = code; + + putBits += huffPutBits; + + putBuffer <<= 24 - putBits; + putBuffer |= huffPutBuffer; + + while (putBits >= 8) + { + + uint8 c = (uint8) (putBuffer >> 16); + + // Output whole bytes we've accumulated with byte stuffing + + EmitByte (c); + + if (c == 0xFF) + { + EmitByte (0); + } + + putBuffer <<= 8; + putBits -= 8; + + } + + huffPutBuffer = putBuffer; + huffPutBits = putBits; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * FlushBits -- + * + * Flush any remaining bits in the bit buffer. Used before emitting + * a marker. + * + * Results: + * None. + * + * Side effects: + * huffPutBuffer and huffPutBits are reset + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::FlushBits () + { + + // The first call forces output of any partial bytes. + + EmitBits (0x007F, 7); + + // We can then zero the buffer. + + huffPutBuffer = 0; + huffPutBits = 0; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * CountOneDiff -- + * + * Count the difference value in countTable. + * + * Results: + * diff is counted in countTable. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +inline void dng_lossless_encoder::CountOneDiff (int diff, uint32 *countTable) + { + + // Encode the DC coefficient difference per section F.1.2.1 + + int temp = diff; + + if (temp < 0) + { + + temp = -temp; + + } + + // Find the number of bits needed for the magnitude of the coefficient + + int nbits = temp >= 256 ? numBitsTable [temp >> 8 ] + 8 + : numBitsTable [temp & 0xFF]; + + // Update count for this bit length + + countTable [nbits] ++; + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * EncodeOneDiff -- + * + * Encode a single difference value. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +inline void dng_lossless_encoder::EncodeOneDiff (int diff, HuffmanTable *dctbl) + { + + // Encode the DC coefficient difference per section F.1.2.1 + + int temp = diff; + int temp2 = diff; + + if (temp < 0) + { + + temp = -temp; + + // For a negative input, want temp2 = bitwise complement of + // abs (input). This code assumes we are on a two's complement + // machine. + + temp2--; + + } + + // Find the number of bits needed for the magnitude of the coefficient + + int nbits = temp >= 256 ? numBitsTable [temp >> 8 ] + 8 + : numBitsTable [temp & 0xFF]; + + // Emit the Huffman-coded symbol for the number of bits + + EmitBits (dctbl->ehufco [nbits], + dctbl->ehufsi [nbits]); + + // Emit that number of bits of the value, if positive, + // or the complement of its magnitude, if negative. + + // If the number of bits is 16, there is only one possible difference + // value (-32786), so the lossless JPEG spec says not to output anything + // in that case. So we only need to output the diference value if + // the number of bits is between 1 and 15. + + if (nbits & 15) + { + + EmitBits (temp2 & (0x0FFFF >> (16 - nbits)), + nbits); + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * FreqCountSet -- + * + * Count the times each category symbol occurs in this image. + * + * Results: + * None. + * + * Side effects: + * The freqCount has counted all category + * symbols appeared in the image. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::FreqCountSet () + { + + memset (freqCount, 0, sizeof (freqCount)); + + DNG_ASSERT ((int32)fSrcRows >= 0, "dng_lossless_encoder::FreqCountSet: fSrcRpws too large."); + + for (int32 row = 0; row < (int32)fSrcRows; row++) + { + + const uint16 *sPtr = fSrcData + row * fSrcRowStep; + + // Initialize predictors for this row. + + int32 predictor [4]; + + for (int32 channel = 0; channel < (int32)fSrcChannels; channel++) + { + + if (row == 0) + predictor [channel] = 1 << (fSrcBitDepth - 1); + + else + predictor [channel] = sPtr [channel - fSrcRowStep]; + + } + + // Unroll most common case of two channels + + if (fSrcChannels == 2) + { + + int32 pred0 = predictor [0]; + int32 pred1 = predictor [1]; + + uint32 srcCols = fSrcCols; + int32 srcColStep = fSrcColStep; + + for (uint32 col = 0; col < srcCols; col++) + { + + int32 pixel0 = sPtr [0]; + int32 pixel1 = sPtr [1]; + + int16 diff0 = (int16) (pixel0 - pred0); + int16 diff1 = (int16) (pixel1 - pred1); + + CountOneDiff (diff0, freqCount [0]); + CountOneDiff (diff1, freqCount [1]); + + pred0 = pixel0; + pred1 = pixel1; + + sPtr += srcColStep; + + } + + } + + // General case. + + else + { + + for (uint32 col = 0; col < fSrcCols; col++) + { + + for (uint32 channel = 0; channel < fSrcChannels; channel++) + { + + int32 pixel = sPtr [channel]; + + int16 diff = (int16) (pixel - predictor [channel]); + + CountOneDiff (diff, freqCount [channel]); + + predictor [channel] = pixel; + + } + + sPtr += fSrcColStep; + + } + + } + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * HuffEncode -- + * + * Encode and output Huffman-compressed image data. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::HuffEncode () + { + + DNG_ASSERT ((int32)fSrcRows >= 0, "dng_lossless_encoder::HuffEncode: fSrcRows too large."); + + for (int32 row = 0; row < (int32)fSrcRows; row++) + { + + const uint16 *sPtr = fSrcData + row * fSrcRowStep; + + // Initialize predictors for this row. + + int32 predictor [4]; + + for (int32 channel = 0; channel < (int32)fSrcChannels; channel++) + { + + if (row == 0) + predictor [channel] = 1 << (fSrcBitDepth - 1); + + else + predictor [channel] = sPtr [channel - fSrcRowStep]; + + } + + // Unroll most common case of two channels + + if (fSrcChannels == 2) + { + + int32 pred0 = predictor [0]; + int32 pred1 = predictor [1]; + + uint32 srcCols = fSrcCols; + int32 srcColStep = fSrcColStep; + + for (uint32 col = 0; col < srcCols; col++) + { + + int32 pixel0 = sPtr [0]; + int32 pixel1 = sPtr [1]; + + int16 diff0 = (int16) (pixel0 - pred0); + int16 diff1 = (int16) (pixel1 - pred1); + + EncodeOneDiff (diff0, &huffTable [0]); + EncodeOneDiff (diff1, &huffTable [1]); + + pred0 = pixel0; + pred1 = pixel1; + + sPtr += srcColStep; + + } + + } + + // General case. + + else + { + + for (uint32 col = 0; col < fSrcCols; col++) + { + + for (uint32 channel = 0; channel < fSrcChannels; channel++) + { + + int32 pixel = sPtr [channel]; + + int16 diff = (int16) (pixel - predictor [channel]); + + EncodeOneDiff (diff, &huffTable [channel]); + + predictor [channel] = pixel; + + } + + sPtr += fSrcColStep; + + } + + } + + } + + FlushBits (); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * GenHuffCoding -- + * + * Generate the optimal coding for the given counts. + * This algorithm is explained in section K.2 of the + * JPEG standard. + * + * Results: + * htbl->bits and htbl->huffval are constructed. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::GenHuffCoding (HuffmanTable *htbl, uint32 *freq) + { + + int i; + int j; + + const int MAX_CLEN = 32; // assumed maximum initial code length + + uint8 bits [MAX_CLEN + 1]; // bits [k] = # of symbols with code length k + short codesize [257]; // codesize [k] = code length of symbol k + short others [257]; // next symbol in current branch of tree + + memset (bits , 0, sizeof (bits )); + memset (codesize, 0, sizeof (codesize)); + + for (i = 0; i < 257; i++) + others [i] = -1; // init links to empty + + // Including the pseudo-symbol 256 in the Huffman procedure guarantees + // that no real symbol is given code-value of all ones, because 256 + // will be placed in the largest codeword category. + + freq [256] = 1; // make sure there is a nonzero count + + // Huffman's basic algorithm to assign optimal code lengths to symbols + + while (true) + { + + // Find the smallest nonzero frequency, set c1 = its symbol. + // In case of ties, take the larger symbol number. + + int c1 = -1; + + uint32 v = 0xFFFFFFFF; + + for (i = 0; i <= 256; i++) + { + + if (freq [i] && freq [i] <= v) + { + v = freq [i]; + c1 = i; + } + + } + + // Find the next smallest nonzero frequency, set c2 = its symbol. + // In case of ties, take the larger symbol number. + + int c2 = -1; + + v = 0xFFFFFFFF; + + for (i = 0; i <= 256; i++) + { + + if (freq [i] && freq [i] <= v && i != c1) + { + v = freq [i]; + c2 = i; + } + + } + + // Done if we've merged everything into one frequency. + + if (c2 < 0) + break; + + // Else merge the two counts/trees. + + freq [c1] += freq [c2]; + freq [c2] = 0; + + // Increment the codesize of everything in c1's tree branch. + + codesize [c1] ++; + + while (others [c1] >= 0) + { + c1 = others [c1]; + codesize [c1] ++; + } + + // chain c2 onto c1's tree branch + + others [c1] = (short) c2; + + // Increment the codesize of everything in c2's tree branch. + + codesize [c2] ++; + + while (others [c2] >= 0) + { + c2 = others [c2]; + codesize [c2] ++; + } + + } + + // Now count the number of symbols of each code length. + + for (i = 0; i <= 256; i++) + { + + if (codesize [i]) + { + + // The JPEG standard seems to think that this can't happen, + // but I'm paranoid... + + if (codesize [i] > MAX_CLEN) + { + + DNG_REPORT ("Huffman code size table overflow"); + + ThrowProgramError (); + + } + + bits [codesize [i]]++; + + } + + } + + // JPEG doesn't allow symbols with code lengths over 16 bits, so if the pure + // Huffman procedure assigned any such lengths, we must adjust the coding. + // Here is what the JPEG spec says about how this next bit works: + // Since symbols are paired for the longest Huffman code, the symbols are + // removed from this length category two at a time. The prefix for the pair + // (which is one bit shorter) is allocated to one of the pair; then, + // skipping the BITS entry for that prefix length, a code word from the next + // shortest nonzero BITS entry is converted into a prefix for two code words + // one bit longer. + + for (i = MAX_CLEN; i > 16; i--) + { + + while (bits [i] > 0) + { + + // Kludge: I have never been able to test this logic, and there + // are comments on the web that this encoder has bugs with 16-bit + // data, so just throw an error if we get here and revert to a + // default table. - tknoll 12/1/03. + + DNG_REPORT ("Info: Optimal huffman table bigger than 16 bits"); + + ThrowProgramError (); + + // Original logic: + + j = i - 2; // find length of new prefix to be used + + while (bits [j] == 0) + j--; + + bits [i ] -= 2; // remove two symbols + bits [i - 1] ++; // one goes in this length + bits [j + 1] += 2; // two new symbols in this length + bits [j ] --; // symbol of this length is now a prefix + + } + + } + + // Remove the count for the pseudo-symbol 256 from + // the largest codelength. + + while (bits [i] == 0) // find largest codelength still in use + i--; + + bits [i] --; + + // Return final symbol counts (only for lengths 0..16). + + memcpy (htbl->bits, bits, sizeof (htbl->bits)); + + // Return a list of the symbols sorted by code length. + // It's not real clear to me why we don't need to consider the codelength + // changes made above, but the JPEG spec seems to think this works. + + int p = 0; + + for (i = 1; i <= MAX_CLEN; i++) + { + + for (j = 0; j <= 255; j++) + { + + if (codesize [j] == i) + { + htbl->huffval [p] = (uint8) j; + p++; + } + + } + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * HuffOptimize -- + * + * Find the best coding parameters for a Huffman-coded scan. + * When called, the scan data has already been converted to + * a sequence of MCU groups of source image samples, which + * are stored in a "big" array, mcuTable. + * + * It counts the times each category symbol occurs. Based on + * this counting, optimal Huffman tables are built. Then it + * uses this optimal Huffman table and counting table to find + * the best PSV. + * + * Results: + * Optimal Huffman tables are retured in cPtr->dcHuffTblPtrs[tbl]. + * Best PSV is retured in cPtr->Ss. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::HuffOptimize () + { + + // Collect the frequency counts. + + FreqCountSet (); + + // Generate Huffman encoding tables. + + for (uint32 channel = 0; channel < fSrcChannels; channel++) + { + + try + { + + GenHuffCoding (&huffTable [channel], freqCount [channel]); + + } + + catch (...) + { + + DNG_REPORT ("Info: Reverting to default huffman table"); + + for (uint32 j = 0; j <= 256; j++) + { + + freqCount [channel] [j] = (j <= 16 ? 1 : 0); + + } + + GenHuffCoding (&huffTable [channel], freqCount [channel]); + + } + + FixHuffTbl (&huffTable [channel]); + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * EmitMarker -- + * + * Emit a marker code into the output stream. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::EmitMarker (JpegMarker mark) + { + + EmitByte (0xFF); + EmitByte ((uint8) mark); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * Emit2bytes -- + * + * Emit a 2-byte integer; these are always MSB first in JPEG + * files + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::Emit2bytes (int value) + { + + EmitByte ((value >> 8) & 0xFF); + EmitByte (value & 0xFF); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * EmitDht -- + * + * Emit a DHT marker, follwed by the huffman data. + * + * Results: + * None + * + * Side effects: + * None + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::EmitDht (int index) + { + + int i; + + HuffmanTable *htbl = &huffTable [index]; + + EmitMarker (M_DHT); + + int length = 0; + + for (i = 1; i <= 16; i++) + length += htbl->bits [i]; + + Emit2bytes (length + 2 + 1 + 16); + + EmitByte ((uint8) index); + + for (i = 1; i <= 16; i++) + EmitByte (htbl->bits [i]); + + for (i = 0; i < length; i++) + EmitByte (htbl->huffval [i]); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * EmitSof -- + * + * Emit a SOF marker plus data. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::EmitSof (JpegMarker code) + { + + EmitMarker (code); + + Emit2bytes (3 * fSrcChannels + 2 + 5 + 1); // length + + EmitByte ((uint8) fSrcBitDepth); + + Emit2bytes (fSrcRows); + Emit2bytes (fSrcCols); + + EmitByte ((uint8) fSrcChannels); + + for (uint32 i = 0; i < fSrcChannels; i++) + { + + EmitByte ((uint8) i); + + EmitByte ((uint8) ((1 << 4) + 1)); // Not subsampled. + + EmitByte (0); // Tq shall be 0 for lossless. + + } + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * EmitSos -- + * + * Emit a SOS marker plus data. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::EmitSos () + { + + EmitMarker (M_SOS); + + Emit2bytes (2 * fSrcChannels + 2 + 1 + 3); // length + + EmitByte ((uint8) fSrcChannels); // Ns + + for (uint32 i = 0; i < fSrcChannels; i++) + { + + // Cs,Td,Ta + + EmitByte ((uint8) i); + EmitByte ((uint8) (i << 4)); + + } + + EmitByte (1); // PSV - hardcoded - tknoll + EmitByte (0); // Spectral selection end - Se + EmitByte (0); // The point transform parameter + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * WriteFileHeader -- + * + * Write the file header. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::WriteFileHeader () + { + + EmitMarker (M_SOI); // first the SOI + + EmitSof (M_SOF3); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * WriteScanHeader -- + * + * Write the start of a scan (everything through the SOS marker). + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::WriteScanHeader () + { + + // Emit Huffman tables. + + for (uint32 i = 0; i < fSrcChannels; i++) + { + + EmitDht (i); + + } + + EmitSos (); + + } + +/*****************************************************************************/ + +/* + *-------------------------------------------------------------- + * + * WriteFileTrailer -- + * + * Write the End of image marker at the end of a JPEG file. + * + * Results: + * None. + * + * Side effects: + * None. + * + *-------------------------------------------------------------- + */ + +void dng_lossless_encoder::WriteFileTrailer () + { + + EmitMarker (M_EOI); + + } + +/*****************************************************************************/ + +void dng_lossless_encoder::Encode () + { + + DNG_ASSERT (fSrcChannels <= 4, "Too many components in scan"); + + // Count the times each difference category occurs. + // Construct the optimal Huffman table. + + HuffOptimize (); + + // Write the frame and scan headers. + + WriteFileHeader (); + + WriteScanHeader (); + + // Encode the image. + + HuffEncode (); + + // Clean up everything. + + WriteFileTrailer (); + + } + +/*****************************************************************************/ + +void EncodeLosslessJPEG (const uint16 *srcData, + uint32 srcRows, + uint32 srcCols, + uint32 srcChannels, + uint32 srcBitDepth, + int32 srcRowStep, + int32 srcColStep, + dng_stream &stream) + { + + dng_lossless_encoder encoder (srcData, + srcRows, + srcCols, + srcChannels, + srcBitDepth, + srcRowStep, + srcColStep, + stream); + + encoder.Encode (); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_lossless_jpeg.h b/source/lib/dng_sdk/dng_lossless_jpeg.h new file mode 100644 index 0000000..be8bb50 --- /dev/null +++ b/source/lib/dng_sdk/dng_lossless_jpeg.h @@ -0,0 +1,69 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_lossless_jpeg.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Functions for encoding and decoding lossless JPEG format. + */ + +/*****************************************************************************/ + +#ifndef __dng_lossless_jpeg__ +#define __dng_lossless_jpeg__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +class dng_spooler + { + + protected: + + virtual ~dng_spooler () + { + } + + public: + + virtual void Spool (const void *data, + uint32 count) = 0; + + }; + +/*****************************************************************************/ + +void DecodeLosslessJPEG (dng_stream &stream, + dng_spooler &spooler, + uint32 minDecodedSize, + uint32 maxDecodedSize, + bool bug16); + +/*****************************************************************************/ + +void EncodeLosslessJPEG (const uint16 *srcData, + uint32 srcRows, + uint32 srcCols, + uint32 srcChannels, + uint32 srcBitDepth, + int32 srcRowStep, + int32 srcColStep, + dng_stream &stream); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_matrix.cpp b/source/lib/dng_sdk/dng_matrix.cpp new file mode 100644 index 0000000..b96f80b --- /dev/null +++ b/source/lib/dng_sdk/dng_matrix.cpp @@ -0,0 +1,1080 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_matrix.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_matrix.h" + +#include "dng_exceptions.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_matrix::dng_matrix () + + : fRows (0) + , fCols (0) + + { + + } + +/*****************************************************************************/ + +dng_matrix::dng_matrix (uint32 rows, + uint32 cols) + + : fRows (0) + , fCols (0) + + { + + if (rows < 1 || rows > kMaxColorPlanes || + cols < 1 || cols > kMaxColorPlanes) + { + + ThrowProgramError (); + + } + + fRows = rows; + fCols = cols; + + for (uint32 row = 0; row < fRows; row++) + for (uint32 col = 0; col < fCols; col++) + { + + fData [row] [col] = 0.0; + + } + + } + +/*****************************************************************************/ + +dng_matrix::dng_matrix (const dng_matrix &m) + + : fRows (m.fRows) + , fCols (m.fCols) + + { + + for (uint32 row = 0; row < fRows; row++) + for (uint32 col = 0; col < fCols; col++) + { + + fData [row] [col] = m.fData [row] [col]; + + } + + } + +/*****************************************************************************/ + +void dng_matrix::Clear () + { + + fRows = 0; + fCols = 0; + + } + +/*****************************************************************************/ + +void dng_matrix::SetIdentity (uint32 count) + { + + *this = dng_matrix (count, count); + + for (uint32 j = 0; j < count; j++) + { + + fData [j] [j] = 1.0; + + } + + } + +/******************************************************************************/ + +bool dng_matrix::operator== (const dng_matrix &m) const + { + + if (Rows () != m.Rows () || + Cols () != m.Cols ()) + { + + return false; + + } + + for (uint32 j = 0; j < Rows (); j++) + for (uint32 k = 0; k < Cols (); k++) + { + + if (fData [j] [k] != m.fData [j] [k]) + { + + return false; + + } + + } + + return true; + + } + +/******************************************************************************/ + +bool dng_matrix::IsDiagonal () const + { + + if (IsEmpty ()) + { + return false; + } + + if (Rows () != Cols ()) + { + return false; + } + + for (uint32 j = 0; j < Rows (); j++) + for (uint32 k = 0; k < Cols (); k++) + { + + if (j != k) + { + + if (fData [j] [k] != 0.0) + { + return false; + } + + } + + } + + return true; + + } + +/******************************************************************************/ + +real64 dng_matrix::MaxEntry () const + { + + if (IsEmpty ()) + { + + return 0.0; + + } + + real64 m = fData [0] [0]; + + for (uint32 j = 0; j < Rows (); j++) + for (uint32 k = 0; k < Cols (); k++) + { + + m = Max_real64 (m, fData [j] [k]); + + } + + return m; + + } + +/******************************************************************************/ + +real64 dng_matrix::MinEntry () const + { + + if (IsEmpty ()) + { + + return 0.0; + + } + + real64 m = fData [0] [0]; + + for (uint32 j = 0; j < Rows (); j++) + for (uint32 k = 0; k < Cols (); k++) + { + + m = Min_real64 (m, fData [j] [k]); + + } + + return m; + + } + +/*****************************************************************************/ + +void dng_matrix::Scale (real64 factor) + { + + for (uint32 j = 0; j < Rows (); j++) + for (uint32 k = 0; k < Cols (); k++) + { + + fData [j] [k] *= factor; + + } + + } + +/*****************************************************************************/ + +void dng_matrix::Round (real64 factor) + { + + real64 invFactor = 1.0 / factor; + + for (uint32 j = 0; j < Rows (); j++) + for (uint32 k = 0; k < Cols (); k++) + { + + fData [j] [k] = Round_int32 (fData [j] [k] * factor) * invFactor; + + } + + } + +/*****************************************************************************/ + +void dng_matrix::SafeRound (real64 factor) + { + + real64 invFactor = 1.0 / factor; + + for (uint32 j = 0; j < Rows (); j++) + { + + // Round each row to the specified accuracy, but make sure the + // a rounding does not affect the total of the elements in a row + // more than necessary. + + real64 error = 0.0; + + for (uint32 k = 0; k < Cols (); k++) + { + + fData [j] [k] += error; + + real64 rounded = Round_int32 (fData [j] [k] * factor) * invFactor; + + error = fData [j] [k] - rounded; + + fData [j] [k] = rounded; + + } + + } + + } + +/*****************************************************************************/ + +dng_matrix_3by3::dng_matrix_3by3 () + + : dng_matrix (3, 3) + + { + } + +/*****************************************************************************/ + +dng_matrix_3by3::dng_matrix_3by3 (const dng_matrix &m) + + : dng_matrix (m) + + { + + if (Rows () != 3 || + Cols () != 3) + { + + ThrowMatrixMath (); + + } + + } + +/*****************************************************************************/ + +dng_matrix_3by3::dng_matrix_3by3 (real64 a00, real64 a01, real64 a02, + real64 a10, real64 a11, real64 a12, + real64 a20, real64 a21, real64 a22) + + + : dng_matrix (3, 3) + + { + + fData [0] [0] = a00; + fData [0] [1] = a01; + fData [0] [2] = a02; + + fData [1] [0] = a10; + fData [1] [1] = a11; + fData [1] [2] = a12; + + fData [2] [0] = a20; + fData [2] [1] = a21; + fData [2] [2] = a22; + + } + +/*****************************************************************************/ + +dng_matrix_3by3::dng_matrix_3by3 (real64 a00, real64 a11, real64 a22) + + : dng_matrix (3, 3) + + { + + fData [0] [0] = a00; + fData [1] [1] = a11; + fData [2] [2] = a22; + + } + +/*****************************************************************************/ + +dng_matrix_4by3::dng_matrix_4by3 () + + : dng_matrix (4, 3) + + { + } + +/*****************************************************************************/ + +dng_matrix_4by3::dng_matrix_4by3 (const dng_matrix &m) + + : dng_matrix (m) + + { + + if (Rows () != 4 || + Cols () != 3) + { + + ThrowMatrixMath (); + + } + + } + +/*****************************************************************************/ + +dng_matrix_4by3::dng_matrix_4by3 (real64 a00, real64 a01, real64 a02, + real64 a10, real64 a11, real64 a12, + real64 a20, real64 a21, real64 a22, + real64 a30, real64 a31, real64 a32) + + + : dng_matrix (4, 3) + + { + + fData [0] [0] = a00; + fData [0] [1] = a01; + fData [0] [2] = a02; + + fData [1] [0] = a10; + fData [1] [1] = a11; + fData [1] [2] = a12; + + fData [2] [0] = a20; + fData [2] [1] = a21; + fData [2] [2] = a22; + + fData [3] [0] = a30; + fData [3] [1] = a31; + fData [3] [2] = a32; + + } + +/*****************************************************************************/ + +dng_vector::dng_vector () + + : fCount (0) + + { + + } + +/*****************************************************************************/ + +dng_vector::dng_vector (uint32 count) + + : fCount (0) + + { + + if (count < 1 || count > kMaxColorPlanes) + { + + ThrowProgramError (); + + } + + fCount = count; + + for (uint32 index = 0; index < fCount; index++) + { + + fData [index] = 0.0; + + } + + } + +/*****************************************************************************/ + +dng_vector::dng_vector (const dng_vector &v) + + : fCount (v.fCount) + + { + + for (uint32 index = 0; index < fCount; index++) + { + + fData [index] = v.fData [index]; + + } + + } + +/*****************************************************************************/ + +void dng_vector::Clear () + { + + fCount = 0; + + } + +/*****************************************************************************/ + +void dng_vector::SetIdentity (uint32 count) + { + + *this = dng_vector (count); + + for (uint32 j = 0; j < count; j++) + { + + fData [j] = 1.0; + + } + + } + +/******************************************************************************/ + +bool dng_vector::operator== (const dng_vector &v) const + { + + if (Count () != v.Count ()) + { + + return false; + + } + + for (uint32 j = 0; j < Count (); j++) + { + + if (fData [j] != v.fData [j]) + { + + return false; + + } + + } + + return true; + + } + +/******************************************************************************/ + +real64 dng_vector::MaxEntry () const + { + + if (IsEmpty ()) + { + + return 0.0; + + } + + real64 m = fData [0]; + + for (uint32 j = 0; j < Count (); j++) + { + + m = Max_real64 (m, fData [j]); + + } + + return m; + + } + +/******************************************************************************/ + +real64 dng_vector::MinEntry () const + { + + if (IsEmpty ()) + { + + return 0.0; + + } + + real64 m = fData [0]; + + for (uint32 j = 0; j < Count (); j++) + { + + m = Min_real64 (m, fData [j]); + + } + + return m; + + } + +/*****************************************************************************/ + +void dng_vector::Scale (real64 factor) + { + + for (uint32 j = 0; j < Count (); j++) + { + + fData [j] *= factor; + + } + + } + +/*****************************************************************************/ + +void dng_vector::Round (real64 factor) + { + + real64 invFactor = 1.0 / factor; + + for (uint32 j = 0; j < Count (); j++) + { + + fData [j] = Round_int32 (fData [j] * factor) * invFactor; + + } + + } + +/*****************************************************************************/ + +dng_matrix dng_vector::AsDiagonal () const + { + + dng_matrix M (Count (), Count ()); + + for (uint32 j = 0; j < Count (); j++) + { + + M [j] [j] = fData [j]; + + } + + return M; + + } + +/*****************************************************************************/ + +dng_matrix dng_vector::AsColumn () const + { + + dng_matrix M (Count (), 1); + + for (uint32 j = 0; j < Count (); j++) + { + + M [j] [0] = fData [j]; + + } + + return M; + + } + +/******************************************************************************/ + +dng_vector_3::dng_vector_3 () + + : dng_vector (3) + + { + } + +/******************************************************************************/ + +dng_vector_3::dng_vector_3 (const dng_vector &v) + + : dng_vector (v) + + { + + if (Count () != 3) + { + + ThrowMatrixMath (); + + } + + } + +/******************************************************************************/ + +dng_vector_3::dng_vector_3 (real64 a0, + real64 a1, + real64 a2) + + : dng_vector (3) + + { + + fData [0] = a0; + fData [1] = a1; + fData [2] = a2; + + } + +/******************************************************************************/ + +dng_vector_4::dng_vector_4 () + + : dng_vector (4) + + { + } + +/******************************************************************************/ + +dng_vector_4::dng_vector_4 (const dng_vector &v) + + : dng_vector (v) + + { + + if (Count () != 4) + { + + ThrowMatrixMath (); + + } + + } + +/******************************************************************************/ + +dng_vector_4::dng_vector_4 (real64 a0, + real64 a1, + real64 a2, + real64 a3) + + : dng_vector (4) + + { + + fData [0] = a0; + fData [1] = a1; + fData [2] = a2; + fData [3] = a3; + + } + +/******************************************************************************/ + +dng_matrix operator* (const dng_matrix &A, + const dng_matrix &B) + { + + if (A.Cols () != B.Rows ()) + { + + ThrowMatrixMath (); + + } + + dng_matrix C (A.Rows (), B.Cols ()); + + for (uint32 j = 0; j < C.Rows (); j++) + for (uint32 k = 0; k < C.Cols (); k++) + { + + C [j] [k] = 0.0; + + for (uint32 m = 0; m < A.Cols (); m++) + { + + real64 aa = A [j] [m]; + + real64 bb = B [m] [k]; + + C [j] [k] += aa * bb; + + } + + } + + return C; + + } + +/******************************************************************************/ + +dng_vector operator* (const dng_matrix &A, + const dng_vector &B) + { + + if (A.Cols () != B.Count ()) + { + + ThrowMatrixMath (); + + } + + dng_vector C (A.Rows ()); + + for (uint32 j = 0; j < C.Count (); j++) + { + + C [j] = 0.0; + + for (uint32 m = 0; m < A.Cols (); m++) + { + + real64 aa = A [j] [m]; + + real64 bb = B [m]; + + C [j] += aa * bb; + + } + + } + + return C; + + } + +/******************************************************************************/ + +dng_matrix operator* (real64 scale, + const dng_matrix &A) + { + + dng_matrix B (A); + + B.Scale (scale); + + return B; + + } + +/******************************************************************************/ + +dng_vector operator* (real64 scale, + const dng_vector &A) + { + + dng_vector B (A); + + B.Scale (scale); + + return B; + + } + +/******************************************************************************/ + +dng_matrix operator+ (const dng_matrix &A, + const dng_matrix &B) + { + + if (A.Cols () != B.Cols () || + A.Rows () != B.Rows ()) + { + + ThrowMatrixMath (); + + } + + dng_matrix C (A); + + for (uint32 j = 0; j < C.Rows (); j++) + for (uint32 k = 0; k < C.Cols (); k++) + { + + C [j] [k] += B [j] [k]; + + } + + return C; + + } + +/******************************************************************************/ + +const real64 kNearZero = 1.0E-10; + +/******************************************************************************/ + +// Work around bug #1294195, which may be a hardware problem on a specific machine. +// This pragma turns on "improved" floating-point consistency. +#ifdef _MSC_VER +#pragma optimize ("p", on) +#endif + +static dng_matrix Invert3by3 (const dng_matrix &A) + { + + real64 a00 = A [0] [0]; + real64 a01 = A [0] [1]; + real64 a02 = A [0] [2]; + real64 a10 = A [1] [0]; + real64 a11 = A [1] [1]; + real64 a12 = A [1] [2]; + real64 a20 = A [2] [0]; + real64 a21 = A [2] [1]; + real64 a22 = A [2] [2]; + + real64 temp [3] [3]; + + temp [0] [0] = a11 * a22 - a21 * a12; + temp [0] [1] = a21 * a02 - a01 * a22; + temp [0] [2] = a01 * a12 - a11 * a02; + temp [1] [0] = a20 * a12 - a10 * a22; + temp [1] [1] = a00 * a22 - a20 * a02; + temp [1] [2] = a10 * a02 - a00 * a12; + temp [2] [0] = a10 * a21 - a20 * a11; + temp [2] [1] = a20 * a01 - a00 * a21; + temp [2] [2] = a00 * a11 - a10 * a01; + + real64 det = (a00 * temp [0] [0] + + a01 * temp [1] [0] + + a02 * temp [2] [0]); + + if (Abs_real64 (det) < kNearZero) + { + + ThrowMatrixMath (); + + } + + dng_matrix B (3, 3); + + for (uint32 j = 0; j < 3; j++) + for (uint32 k = 0; k < 3; k++) + { + + B [j] [k] = temp [j] [k] / det; + + } + + return B; + + } + +// Reset floating-point optimization. See comment above. +#ifdef _MSC_VER +#pragma optimize ("p", off) +#endif + +/******************************************************************************/ + +static dng_matrix InvertNbyN (const dng_matrix &A) + { + + uint32 i; + uint32 j; + uint32 k; + + uint32 n = A.Rows (); + + real64 temp [kMaxColorPlanes] [kMaxColorPlanes * 2]; + + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) + { + + temp [i] [j ] = A [i] [j]; + + temp [i] [j + n] = (i == j ? 1.0 : 0.0); + + } + + for (i = 0; i < n; i++) + { + + real64 alpha = temp [i] [i]; + + if (Abs_real64 (alpha) < kNearZero) + { + + ThrowMatrixMath (); + + } + + for (j = 0; j < n * 2; j++) + { + + temp [i] [j] /= alpha; + + } + + for (k = 0; k < n; k++) + { + + if (i != k) + { + + real64 beta = temp [k] [i]; + + for (j = 0; j < n * 2; j++) + { + + temp [k] [j] -= beta * temp [i] [j]; + + } + + } + + } + + } + + dng_matrix B (n, n); + + for (i = 0; i < n; i++) + for (j = 0; j < n; j++) + { + + B [i] [j] = temp [i] [j + n]; + + } + + return B; + + } + +/******************************************************************************/ + +dng_matrix Transpose (const dng_matrix &A) + { + + dng_matrix B (A.Cols (), A.Rows ()); + + for (uint32 j = 0; j < B.Rows (); j++) + for (uint32 k = 0; k < B.Cols (); k++) + { + + B [j] [k] = A [k] [j]; + + } + + return B; + + } + +/******************************************************************************/ + +dng_matrix Invert (const dng_matrix &A) + { + + if (A.Rows () < 2 || A.Cols () < 2) + { + + ThrowMatrixMath (); + + } + + if (A.Rows () == A.Cols ()) + { + + if (A.Rows () == 3) + { + + return Invert3by3 (A); + + } + + return InvertNbyN (A); + + } + + else + { + + // Compute the pseudo inverse. + + dng_matrix B = Transpose (A); + + return Invert (B * A) * B; + + } + + } + +/******************************************************************************/ + +dng_matrix Invert (const dng_matrix &A, + const dng_matrix &hint) + { + + if (A.Rows () == A .Cols () || + A.Rows () != hint.Cols () || + A.Cols () != hint.Rows ()) + { + + return Invert (A); + + } + + else + { + + // Use the specified hint matrix. + + return Invert (hint * A) * hint; + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_matrix.h b/source/lib/dng_sdk/dng_matrix.h new file mode 100644 index 0000000..cdac55a --- /dev/null +++ b/source/lib/dng_sdk/dng_matrix.h @@ -0,0 +1,326 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_matrix.h#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * Matrix and vector classes, including specialized 3x3 and 4x3 versions as + * well as length 3 vectors. + */ + +/*****************************************************************************/ + +#ifndef __dng_matrix__ +#define __dng_matrix__ + +/*****************************************************************************/ + +#include "dng_sdk_limits.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief Class to represent 2D matrix up to kMaxColorPlanes x kMaxColorPlanes +/// in size. + +class dng_matrix + { + + protected: + + uint32 fRows; + uint32 fCols; + + real64 fData [kMaxColorPlanes] [kMaxColorPlanes]; + + public: + + dng_matrix (); + + dng_matrix (uint32 rows, + uint32 cols); + + dng_matrix (const dng_matrix &m); + + virtual ~dng_matrix () + { + } + + void Clear (); + + void SetIdentity (uint32 count); + + uint32 Rows () const + { + return fRows; + } + + uint32 Cols () const + { + return fCols; + } + + real64 * operator [] (uint32 row) + { + return fData [row]; + } + + const real64 * operator [] (uint32 row) const + { + return fData [row]; + } + + bool operator== (const dng_matrix &m) const; + + bool operator!= (const dng_matrix &m) const + { + return !(*this == m); + } + + bool IsEmpty () const + { + return fRows == 0 || fCols == 0; + } + + bool NotEmpty () const + { + return !IsEmpty (); + } + + bool IsDiagonal () const; + + real64 MaxEntry () const; + + real64 MinEntry () const; + + void Scale (real64 factor); + + void Round (real64 factor); + + void SafeRound (real64 factor); + + }; + +/*****************************************************************************/ + +/// \brief A 3x3 matrix. + +class dng_matrix_3by3: public dng_matrix + { + + public: + + dng_matrix_3by3 (); + + dng_matrix_3by3 (const dng_matrix &m); + + dng_matrix_3by3 (real64 a00, real64 a01, real64 a02, + real64 a10, real64 a11, real64 a12, + real64 a20, real64 a21, real64 a22); + + dng_matrix_3by3 (real64 a00, real64 a11, real64 a22); + + }; + +/*****************************************************************************/ + +/// \brief A 4x3 matrix. Handy for working with 4-color cameras. + +class dng_matrix_4by3: public dng_matrix + { + + public: + + dng_matrix_4by3 (); + + dng_matrix_4by3 (const dng_matrix &m); + + dng_matrix_4by3 (real64 a00, real64 a01, real64 a02, + real64 a10, real64 a11, real64 a12, + real64 a20, real64 a21, real64 a22, + real64 a30, real64 a31, real64 a32); + + }; + +/*****************************************************************************/ + +/// \brief Class to represent 1-dimensional vector with up to kMaxColorPlanes +/// components. + +class dng_vector + { + + protected: + + uint32 fCount; + + real64 fData [kMaxColorPlanes]; + + public: + + dng_vector (); + + dng_vector (uint32 count); + + dng_vector (const dng_vector &v); + + virtual ~dng_vector () + { + } + + void Clear (); + + void SetIdentity (uint32 count); + + uint32 Count () const + { + return fCount; + } + + real64 & operator [] (uint32 index) + { + return fData [index]; + } + + const real64 & operator [] (uint32 index) const + { + return fData [index]; + } + + bool operator== (const dng_vector &v) const; + + bool operator!= (const dng_vector &v) const + { + return !(*this == v); + } + + bool IsEmpty () const + { + return fCount == 0; + } + + bool NotEmpty () const + { + return !IsEmpty (); + } + + real64 MaxEntry () const; + + real64 MinEntry () const; + + void Scale (real64 factor); + + void Round (real64 factor); + + dng_matrix AsDiagonal () const; + + dng_matrix AsColumn () const; + + }; + +/*****************************************************************************/ + +/// \brief A 3-element vector. + +class dng_vector_3: public dng_vector + { + + public: + + dng_vector_3 (); + + dng_vector_3 (const dng_vector &v); + + dng_vector_3 (real64 a0, + real64 a1, + real64 a2); + + }; + +/*****************************************************************************/ + +/// \brief A 4-element vector. + +class dng_vector_4: public dng_vector + { + + public: + + dng_vector_4 (); + + dng_vector_4 (const dng_vector &v); + + dng_vector_4 (real64 a0, + real64 a1, + real64 a2, + real64 a3); + + }; + +/*****************************************************************************/ + +dng_matrix operator* (const dng_matrix &A, + const dng_matrix &B); + +dng_vector operator* (const dng_matrix &A, + const dng_vector &B); + +dng_matrix operator* (real64 scale, + const dng_matrix &A); + +dng_vector operator* (real64 scale, + const dng_vector &A); + +/*****************************************************************************/ + +dng_matrix operator+ (const dng_matrix &A, + const dng_matrix &B); + +/*****************************************************************************/ + +dng_matrix Transpose (const dng_matrix &A); + +/*****************************************************************************/ + +dng_matrix Invert (const dng_matrix &A); + +dng_matrix Invert (const dng_matrix &A, + const dng_matrix &hint); + +/*****************************************************************************/ + +inline real64 MaxEntry (const dng_matrix &A) + { + return A.MaxEntry (); + } + +inline real64 MaxEntry (const dng_vector &A) + { + return A.MaxEntry (); + } + +/*****************************************************************************/ + +inline real64 MinEntry (const dng_matrix &A) + { + return A.MinEntry (); + } + +inline real64 MinEntry (const dng_vector &A) + { + return A.MinEntry (); + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_memory.cpp b/source/lib/dng_sdk/dng_memory.cpp new file mode 100644 index 0000000..8934e89 --- /dev/null +++ b/source/lib/dng_sdk/dng_memory.cpp @@ -0,0 +1,204 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_memory.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_memory.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" + +#include "gpr_allocator.h" + +/*****************************************************************************/ + +dng_memory_data::dng_memory_data () + + : fBuffer (NULL) + + { + + } + +/*****************************************************************************/ + +dng_memory_data::dng_memory_data (uint32 size) + + : fBuffer (NULL) + + { + + Allocate (size); + + } + +/*****************************************************************************/ + +dng_memory_data::~dng_memory_data () + { + + Clear (); + + } + +/*****************************************************************************/ + +void dng_memory_data::Allocate (uint32 size) + { + + Clear (); + + if (size) + { + + fBuffer = (char*)gpr_global_malloc(size); + + if (!fBuffer) + { + + ThrowMemoryFull (); + + } + + } + + } + +/*****************************************************************************/ + +void dng_memory_data::Clear () + { + + if (fBuffer) + { + gpr_global_free(fBuffer); + + fBuffer = NULL; + + } + + } + +/*****************************************************************************/ + +dng_memory_block * dng_memory_block::Clone (dng_memory_allocator &allocator) const + { + + uint32 size = LogicalSize (); + + dng_memory_block * result = allocator.Allocate (size); + + DoCopyBytes (Buffer (), result->Buffer (), size); + + return result; + + } + +/*****************************************************************************/ + +class dng_malloc_block : public dng_memory_block + { + + private: + + void *fMalloc; + + public: + + dng_malloc_block (uint32 logicalSize); + + virtual ~dng_malloc_block (); + + private: + + // Hidden copy constructor and assignment operator. + + dng_malloc_block (const dng_malloc_block &block); + + dng_malloc_block & operator= (const dng_malloc_block &block); + + }; + +/*****************************************************************************/ + +dng_malloc_block::dng_malloc_block (uint32 logicalSize) + + : dng_memory_block (logicalSize) + + , fMalloc (NULL) + + { + +#if qLinux + + int err = ::posix_memalign( (void **) &fMalloc, 16, (size_t) PhysicalSize() ); + + if (err) + { + + ThrowMemoryFull (); + + } + +#else + + fMalloc = (char*)gpr_global_malloc( PhysicalSize ()); + + if (!fMalloc) + { + + ThrowMemoryFull (); + + } + +#endif // qLinux + + SetBuffer (fMalloc); + + } + +/*****************************************************************************/ + +dng_malloc_block::~dng_malloc_block () + { + + if (fMalloc) + { + gpr_global_free( fMalloc ); + } + + } + +/*****************************************************************************/ + +dng_memory_block * dng_memory_allocator::Allocate (uint32 size) + { + + dng_memory_block *result = new dng_malloc_block (size); + + if (!result) + { + + ThrowMemoryFull (); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_memory_allocator gDefaultDNGMemoryAllocator; + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_memory.h b/source/lib/dng_sdk/dng_memory.h new file mode 100644 index 0000000..809e68a --- /dev/null +++ b/source/lib/dng_sdk/dng_memory.h @@ -0,0 +1,515 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_memory.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** Support for memory allocation. + */ + +/*****************************************************************************/ + +#ifndef __dng_memory__ +#define __dng_memory__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief Class to provide resource acquisition is instantiation discipline +/// for small memory allocations. +/// +/// This class does not use dng_memory_allocator for memory allocation. + +class dng_memory_data + { + + private: + + char *fBuffer; + + public: + + /// Construct an empty memory buffer using malloc. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + dng_memory_data (); + + /// Construct memory buffer of size bytes using malloc. + /// \param size Number of bytes of memory needed. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + dng_memory_data (uint32 size); + + /// Release memory buffer using free. + + ~dng_memory_data (); + + /// Clear existing memory buffer and allocate new memory of size bytes. + /// \param size Number of bytes of memory needed. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + void Allocate (uint32 size); + + /// Release any allocated memory using free. Object is still valid and + /// Allocate can be called again. + + void Clear (); + + /// Return pointer to allocated memory as a void *.. + /// \retval void * valid for as many bytes as were allocated. + + void * Buffer () + { + return fBuffer; + } + + /// Return pointer to allocated memory as a const void *. + /// \retval const void * valid for as many bytes as were allocated. + + const void * Buffer () const + { + return fBuffer; + } + + /// Return pointer to allocated memory as a char *. + /// \retval char * valid for as many bytes as were allocated. + + char * Buffer_char () + { + return (char *) Buffer (); + } + + /// Return pointer to allocated memory as a const char *. + /// \retval const char * valid for as many bytes as were allocated. + + const char * Buffer_char () const + { + return (const char *) Buffer (); + } + + /// Return pointer to allocated memory as a uint8 *. + /// \retval uint8 * valid for as many bytes as were allocated. + + uint8 * Buffer_uint8 () + { + return (uint8 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint8 *. + /// \retval const uint8 * valid for as many bytes as were allocated. + + const uint8 * Buffer_uint8 () const + { + return (const uint8 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint16 *. + /// \retval uint16 * valid for as many bytes as were allocated. + + uint16 * Buffer_uint16 () + { + return (uint16 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint16 *. + /// \retval const uint16 * valid for as many bytes as were allocated. + + const uint16 * Buffer_uint16 () const + { + return (const uint16 *) Buffer (); + } + + /// Return pointer to allocated memory as a int16 *. + /// \retval int16 * valid for as many bytes as were allocated. + + int16 * Buffer_int16 () + { + return (int16 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int16 *. + /// \retval const int16 * valid for as many bytes as were allocated. + + const int16 * Buffer_int16 () const + { + return (const int16 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint32 *. + /// \retval uint32 * valid for as many bytes as were allocated. + + uint32 * Buffer_uint32 () + { + return (uint32 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint32 *. + /// \retval uint32 * valid for as many bytes as were allocated. + + const uint32 * Buffer_uint32 () const + { + return (const uint32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int32 *. + /// \retval const int32 * valid for as many bytes as were allocated. + + int32 * Buffer_int32 () + { + return (int32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int32 *. + /// \retval const int32 * valid for as many bytes as were allocated. + + const int32 * Buffer_int32 () const + { + return (const int32 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint64 *. + /// \retval uint64 * valid for as many bytes as were allocated. + + uint64 * Buffer_uint64 () + { + return (uint64 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint64 *. + /// \retval uint64 * valid for as many bytes as were allocated. + + const uint64 * Buffer_uint64 () const + { + return (const uint64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int64 *. + /// \retval const int64 * valid for as many bytes as were allocated. + + int64 * Buffer_int64 () + { + return (int64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int64 *. + /// \retval const int64 * valid for as many bytes as were allocated. + + const int64 * Buffer_int64 () const + { + return (const int64 *) Buffer (); + } + + /// Return pointer to allocated memory as a real32 *. + /// \retval real32 * valid for as many bytes as were allocated. + + real32 * Buffer_real32 () + { + return (real32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const real32 *. + /// \retval const real32 * valid for as many bytes as were allocated. + + const real32 * Buffer_real32 () const + { + return (const real32 *) Buffer (); + } + + /// Return pointer to allocated memory as a real64 *. + /// \retval real64 * valid for as many bytes as were allocated. + + real64 * Buffer_real64 () + { + return (real64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const real64 *. + /// \retval const real64 * valid for as many bytes as were allocated. + + const real64 * Buffer_real64 () const + { + return (const real64 *) Buffer (); + } + + private: + + // Hidden copy constructor and assignment operator. + + dng_memory_data (const dng_memory_data &data); + + dng_memory_data & operator= (const dng_memory_data &data); + + }; + +/*****************************************************************************/ + +/// \brief Class to provide resource acquisition is instantiation discipline for +/// image buffers and other larger memory allocations. +/// +/// This class requires a dng_memory_allocator for allocation. + +class dng_memory_block + { + + private: + + uint32 fLogicalSize; + + char *fBuffer; + + protected: + + dng_memory_block (uint32 logicalSize) + : fLogicalSize (logicalSize) + , fBuffer (NULL) + { + } + + uint32 PhysicalSize () + { + + // This size is padded for TWO reasons! The first is allow alignment + // to 16-byte boundaries if the allocator does not do that already. The + // second, which is very important, so to provide safe overread areas for + // SSE2-type bottlenecks, which can often be written faster by allowing them + // to reading slightly block. Someone on the image core them did not + // understand this and removed this padding. I'm undoing this removal + // and restoring this padding, since removing it might lead to memory + // access crashes in some cases. + + // This padding is throwing off all of our allocations (f.e. dng_string, pixel buffers, etc) + // that uses dng_memory_block on iOS/Android that is memory limited. Imagecore carefully + // allocates pow2 tile buffers, but this bumps us to the next ssd block (+4K). + // This also makes it difficult to identify memory reports in Instruments since all + // numbers are off by 64. Imagecore never crashed from the removal of the padding. + // The allocator on Win64/Mac64 is 16-byte aligned already. iOS is too. + // Linux is 8 byte, but it's using mem_align. + // We should fix the SIMD routines and revisit removing this padding - Alec. + + return fLogicalSize + 64; + + } + + void SetBuffer (void *p) + { + fBuffer = (char *) ((((uintptr) p) + 15) & ~((uintptr) 15)); + } + + public: + + virtual ~dng_memory_block () + { + } + + dng_memory_block * Clone (dng_memory_allocator &allocator) const; + + /// Getter for available size, in bytes, of memory block. + /// \retval size in bytes of available memory in memory block. + + uint32 LogicalSize () const + { + return fLogicalSize; + } + + /// Return pointer to allocated memory as a void *.. + /// \retval void * valid for as many bytes as were allocated. + + void * Buffer () + { + return fBuffer; + } + + /// Return pointer to allocated memory as a const void *. + /// \retval const void * valid for as many bytes as were allocated. + + const void * Buffer () const + { + return fBuffer; + } + + /// Return pointer to allocated memory as a char *. + /// \retval char * valid for as many bytes as were allocated. + + char * Buffer_char () + { + return (char *) Buffer (); + } + + /// Return pointer to allocated memory as a const char *. + /// \retval const char * valid for as many bytes as were allocated. + + const char * Buffer_char () const + { + return (const char *) Buffer (); + } + + /// Return pointer to allocated memory as a uint8 *. + /// \retval uint8 * valid for as many bytes as were allocated. + + uint8 * Buffer_uint8 () + { + return (uint8 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint8 *. + /// \retval const uint8 * valid for as many bytes as were allocated. + + const uint8 * Buffer_uint8 () const + { + return (const uint8 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint16 *. + /// \retval uint16 * valid for as many bytes as were allocated. + + uint16 * Buffer_uint16 () + { + return (uint16 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint16 *. + /// \retval const uint16 * valid for as many bytes as were allocated. + + const uint16 * Buffer_uint16 () const + { + return (const uint16 *) Buffer (); + } + + /// Return pointer to allocated memory as a int16 *. + /// \retval int16 * valid for as many bytes as were allocated. + + int16 * Buffer_int16 () + { + return (int16 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int16 *. + /// \retval const int16 * valid for as many bytes as were allocated. + + const int16 * Buffer_int16 () const + { + return (const int16 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint32 *. + /// \retval uint32 * valid for as many bytes as were allocated. + + uint32 * Buffer_uint32 () + { + return (uint32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint32 *. + /// \retval const uint32 * valid for as many bytes as were allocated. + + const uint32 * Buffer_uint32 () const + { + return (const uint32 *) Buffer (); + } + + /// Return pointer to allocated memory as a int32 *. + /// \retval int32 * valid for as many bytes as were allocated. + + int32 * Buffer_int32 () + { + return (int32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int32 *. + /// \retval const int32 * valid for as many bytes as were allocated. + + const int32 * Buffer_int32 () const + { + return (const int32 *) Buffer (); + } + + /// Return pointer to allocated memory as a real32 *. + /// \retval real32 * valid for as many bytes as were allocated. + + real32 * Buffer_real32 () + { + return (real32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const real32 *. + /// \retval const real32 * valid for as many bytes as were allocated. + + const real32 * Buffer_real32 () const + { + return (const real32 *) Buffer (); + } + + /// Return pointer to allocated memory as a real64 *. + /// \retval real64 * valid for as many bytes as were allocated. + + real64 * Buffer_real64 () + { + return (real64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const real64 *. + /// \retval const real64 * valid for as many bytes as were allocated. + + const real64 * Buffer_real64 () const + { + return (const real64 *) Buffer (); + } + + private: + + // Hidden copy constructor and assignment operator. + + dng_memory_block (const dng_memory_block &data); + + dng_memory_block & operator= (const dng_memory_block &data); + + }; + +/*****************************************************************************/ + +/// \brief Interface for dng_memory_block allocator. + +class dng_memory_allocator + { + + public: + + virtual ~dng_memory_allocator () + { + } + + /// Allocate a dng_memory block. + /// \param size Number of bytes in memory block. + /// \retval A dng_memory_block with at least size bytes of valid storage. + /// \exception dng_exception with fErrorCode equal to dng_error_memory. + + virtual dng_memory_block * Allocate (uint32 size); + + }; + +/*****************************************************************************/ + +/// \brief Default memory allocator used if NULL is passed in for allocator +/// when constructing a dng_host. +/// +/// Uses new and delete for memory block object and malloc/free for underlying +/// buffer. + +extern dng_memory_allocator gDefaultDNGMemoryAllocator; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_memory_stream.cpp b/source/lib/dng_sdk/dng_memory_stream.cpp new file mode 100644 index 0000000..9368d67 --- /dev/null +++ b/source/lib/dng_sdk/dng_memory_stream.cpp @@ -0,0 +1,253 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_memory_stream.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_memory_stream.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_utils.h" + +#include "gpr_allocator.h" + +/*****************************************************************************/ + +dng_memory_stream::dng_memory_stream (dng_memory_allocator &allocator, + dng_abort_sniffer *sniffer, + uint32 pageSize) + + : dng_stream (sniffer, + kDefaultBufferSize, + kDNGStreamInvalidOffset) + + , fAllocator (allocator) + , fPageSize (pageSize ) + + , fPageCount (0) + , fPagesAllocated (0) + , fPageList (NULL) + + , fMemoryStreamLength (0) + + { + + } + +/*****************************************************************************/ + +dng_memory_stream::~dng_memory_stream () + { + + if (fPageList) + { + + for (uint32 index = 0; index < fPageCount; index++) + { + + delete fPageList [index]; + + } + + gpr_global_free( fPageList ); + + } + + } + +/*****************************************************************************/ + +uint64 dng_memory_stream::DoGetLength () + { + + return fMemoryStreamLength; + + } + +/*****************************************************************************/ + +void dng_memory_stream::DoRead (void *data, + uint32 count, + uint64 offset) + { + + if (offset + count > fMemoryStreamLength) + { + + ThrowEndOfFile (); + + } + + uint64 baseOffset = offset; + + while (count) + { + + uint32 pageIndex = (uint32) (offset / fPageSize); + uint32 pageOffset = (uint32) (offset % fPageSize); + + uint32 blockCount = Min_uint32 (fPageSize - pageOffset, count); + + const uint8 *sPtr = fPageList [pageIndex]->Buffer_uint8 () + + pageOffset; + + uint8 *dPtr = ((uint8 *) data) + (uint32) (offset - baseOffset); + + DoCopyBytes (sPtr, dPtr, blockCount); + + offset += blockCount; + count -= blockCount; + + } + + } + +/*****************************************************************************/ + +void dng_memory_stream::DoSetLength (uint64 length) + { + + while (length > fPageCount * (uint64) fPageSize) + { + + if (fPageCount == fPagesAllocated) + { + + uint32 newSize = Max_uint32 (fPagesAllocated + 32, + fPagesAllocated * 2); + + dng_memory_block **list = (dng_memory_block **) gpr_global_malloc( newSize * sizeof (dng_memory_block *) ); + + if (!list) + { + + ThrowMemoryFull (); + + } + + if (fPageCount) + { + + DoCopyBytes (fPageList, + list, + fPageCount * (uint32) sizeof (dng_memory_block *)); + + } + + if (fPageList) + { + gpr_global_free( fPageList ); + } + + fPageList = list; + + fPagesAllocated = newSize; + + } + + fPageList [fPageCount] = fAllocator.Allocate (fPageSize); + + fPageCount++; + + } + + fMemoryStreamLength = length; + + } + +/*****************************************************************************/ + +void dng_memory_stream::DoWrite (const void *data, + uint32 count, + uint64 offset) + { + + DoSetLength (Max_uint64 (fMemoryStreamLength, + offset + count)); + + uint64 baseOffset = offset; + + while (count) + { + + uint32 pageIndex = (uint32) (offset / fPageSize); + uint32 pageOffset = (uint32) (offset % fPageSize); + + uint32 blockCount = Min_uint32 (fPageSize - pageOffset, count); + + const uint8 *sPtr = ((const uint8 *) data) + (uint32) (offset - baseOffset); + + uint8 *dPtr = fPageList [pageIndex]->Buffer_uint8 () + + pageOffset; + + DoCopyBytes (sPtr, dPtr, blockCount); + + offset += blockCount; + count -= blockCount; + + } + + } + +/*****************************************************************************/ + +void dng_memory_stream::CopyToStream (dng_stream &dstStream, + uint64 count) + { + + if (count < kBigBufferSize) + { + + dng_stream::CopyToStream (dstStream, count); + + } + + else + { + + Flush (); + + uint64 offset = Position (); + + if (offset + count > Length ()) + { + + ThrowEndOfFile (); + + } + + while (count) + { + + uint32 pageIndex = (uint32) (offset / fPageSize); + uint32 pageOffset = (uint32) (offset % fPageSize); + + uint32 blockCount = (uint32) Min_uint64 (fPageSize - pageOffset, count); + + const uint8 *sPtr = fPageList [pageIndex]->Buffer_uint8 () + + pageOffset; + + dstStream.Put (sPtr, blockCount); + + offset += blockCount; + count -= blockCount; + + } + + SetReadPosition (offset); + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_memory_stream.h b/source/lib/dng_sdk/dng_memory_stream.h new file mode 100644 index 0000000..4477936 --- /dev/null +++ b/source/lib/dng_sdk/dng_memory_stream.h @@ -0,0 +1,97 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_memory_stream.h#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * Stream abstraction to/from in-memory data. + */ + +/*****************************************************************************/ + +#ifndef __dng_memory_stream__ +#define __dng_memory_stream__ + +/*****************************************************************************/ + +#include "dng_stream.h" + +/*****************************************************************************/ + +/// \brief A dng_stream which can be read from or written to memory. +/// +/// Stream is populated via writing and either read or accessed by asking for contents as a pointer. + +class dng_memory_stream: public dng_stream + { + + protected: + + dng_memory_allocator &fAllocator; + + uint32 fPageSize; + + uint32 fPageCount; + uint32 fPagesAllocated; + + dng_memory_block **fPageList; + + uint64 fMemoryStreamLength; + + public: + + /// Construct a new memory-based stream. + /// \param allocator Allocator to use to allocate memory in stream as needed. + /// \param sniffer If non-NULL used to check for user cancellation. + /// \param pageSize Unit of allocation for data stored in stream. + + dng_memory_stream (dng_memory_allocator &allocator, + dng_abort_sniffer *sniffer = NULL, + uint32 pageSize = 64 * 1024); + + virtual ~dng_memory_stream (); + + /// Copy a specified number of bytes to a target stream. + /// \param dstStream The target stream. + /// \param count The number of bytes to copy. + + virtual void CopyToStream (dng_stream &dstStream, + uint64 count); + + protected: + + virtual uint64 DoGetLength (); + + virtual void DoRead (void *data, + uint32 count, + uint64 offset); + + virtual void DoSetLength (uint64 length); + + virtual void DoWrite (const void *data, + uint32 count, + uint64 offset); + + private: + + // Hidden copy constructor and assignment operator. + + dng_memory_stream (const dng_memory_stream &stream); + + dng_memory_stream & operator= (const dng_memory_stream &stream); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_misc_opcodes.cpp b/source/lib/dng_sdk/dng_misc_opcodes.cpp new file mode 100644 index 0000000..139b79f --- /dev/null +++ b/source/lib/dng_sdk/dng_misc_opcodes.cpp @@ -0,0 +1,1578 @@ +/*****************************************************************************/ +// Copyright 2008-2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_misc_opcodes.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_misc_opcodes.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_rect.h" +#include "dng_stream.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +dng_opcode_TrimBounds::dng_opcode_TrimBounds (const dng_rect &bounds) + + : dng_opcode (dngOpcode_TrimBounds, + dngVersion_1_3_0_0, + kFlag_None) + + , fBounds (bounds) + + { + + } + +/*****************************************************************************/ + +dng_opcode_TrimBounds::dng_opcode_TrimBounds (dng_stream &stream) + + : dng_opcode (dngOpcode_TrimBounds, + stream, + "TrimBounds") + + , fBounds () + + { + + if (stream.Get_uint32 () != 16) + { + ThrowBadFormat (); + } + + fBounds.t = stream.Get_int32 (); + fBounds.l = stream.Get_int32 (); + fBounds.b = stream.Get_int32 (); + fBounds.r = stream.Get_int32 (); + + if (fBounds.IsEmpty ()) + { + ThrowBadFormat (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Bounds: t=%d, l=%d, b=%d, r=%d\n", + (int) fBounds.t, + (int) fBounds.l, + (int) fBounds.b, + (int) fBounds.r); + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_TrimBounds::PutData (dng_stream &stream) const + { + + stream.Put_uint32 (16); + + stream.Put_int32 (fBounds.t); + stream.Put_int32 (fBounds.l); + stream.Put_int32 (fBounds.b); + stream.Put_int32 (fBounds.r); + + } + +/*****************************************************************************/ + +void dng_opcode_TrimBounds::Apply (dng_host & /* host */, + dng_negative & /* negative */, + AutoPtr &image) + { + + if (fBounds.IsEmpty () || (fBounds & image->Bounds ()) != fBounds) + { + ThrowBadFormat (); + } + + image->Trim (fBounds); + + } + +/*****************************************************************************/ + +void dng_area_spec::GetData (dng_stream &stream) + { + + fArea.t = stream.Get_int32 (); + fArea.l = stream.Get_int32 (); + fArea.b = stream.Get_int32 (); + fArea.r = stream.Get_int32 (); + + fPlane = stream.Get_uint32 (); + fPlanes = stream.Get_uint32 (); + + fRowPitch = stream.Get_uint32 (); + fColPitch = stream.Get_uint32 (); + + if (fPlanes < 1) + { + ThrowBadFormat (); + } + + if (fRowPitch < 1 || fColPitch < 1) + { + ThrowBadFormat (); + } + + if (fArea.IsEmpty () && (fRowPitch != 1 || fColPitch != 1)) + { + ThrowBadFormat (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AreaSpec: t=%d, l=%d, b=%d, r=%d, p=%u:%u, rp=%u, cp=%u\n", + (int) fArea.t, + (int) fArea.l, + (int) fArea.b, + (int) fArea.r, + (unsigned) fPlane, + (unsigned) fPlanes, + (unsigned) fRowPitch, + (unsigned) fColPitch); + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_area_spec::PutData (dng_stream &stream) const + { + + stream.Put_int32 (fArea.t); + stream.Put_int32 (fArea.l); + stream.Put_int32 (fArea.b); + stream.Put_int32 (fArea.r); + + stream.Put_uint32 (fPlane); + stream.Put_uint32 (fPlanes); + + stream.Put_uint32 (fRowPitch); + stream.Put_uint32 (fColPitch); + + } + +/*****************************************************************************/ + +dng_rect dng_area_spec::Overlap (const dng_rect &tile) const + { + + // Special case - if the fArea is empty, then dng_area_spec covers + // the entire image, no matter how large it is. + + if (fArea.IsEmpty ()) + { + return tile; + } + + dng_rect overlap = fArea & tile; + + if (overlap.NotEmpty ()) + { + + overlap.t = fArea.t + ((overlap.t - fArea.t + fRowPitch - 1) / fRowPitch) * fRowPitch; + overlap.l = fArea.l + ((overlap.l - fArea.l + fColPitch - 1) / fColPitch) * fColPitch; + + if (overlap.NotEmpty ()) + { + + overlap.b = overlap.t + ((overlap.H () - 1) / fRowPitch) * fRowPitch + 1; + overlap.r = overlap.l + ((overlap.W () - 1) / fColPitch) * fColPitch + 1; + + return overlap; + + } + + } + + return dng_rect (); + + } + +/*****************************************************************************/ + +dng_opcode_MapTable::dng_opcode_MapTable (dng_host &host, + const dng_area_spec &areaSpec, + const uint16 *table, + uint32 count) + + : dng_inplace_opcode (dngOpcode_MapTable, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + , fTable () + , fCount (count) + + { + + if (count == 0 || count > 0x10000) + { + ThrowProgramError (); + } + + fTable.Reset (host.Allocate (0x10000 * sizeof (uint16))); + + DoCopyBytes (table, + fTable->Buffer (), + count * (uint32) sizeof (uint16)); + + ReplicateLastEntry (); + + } + +/*****************************************************************************/ + +dng_opcode_MapTable::dng_opcode_MapTable (dng_host &host, + dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_MapTable, + stream, + "MapTable") + + , fAreaSpec () + , fTable () + , fCount (0) + + { + + uint32 dataSize = stream.Get_uint32 (); + + fAreaSpec.GetData (stream); + + fCount = stream.Get_uint32 (); + + if (dataSize != dng_area_spec::kDataSize + 4 + fCount * 2) + { + ThrowBadFormat (); + } + + if (fCount == 0 || fCount > 0x10000) + { + ThrowBadFormat (); + } + + fTable.Reset (host.Allocate (0x10000 * sizeof (uint16))); + + uint16 *table = fTable->Buffer_uint16 (); + + for (uint32 index = 0; index < fCount; index++) + { + table [index] = stream.Get_uint16 (); + } + + ReplicateLastEntry (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Count: %u\n", (unsigned) fCount); + + for (uint32 j = 0; j < fCount && j < gDumpLineLimit; j++) + { + printf (" Table [%5u] = %5u\n", (unsigned) j, (unsigned) table [j]); + } + + if (fCount > gDumpLineLimit) + { + printf (" ... %u table entries skipped\n", (unsigned) (fCount - gDumpLineLimit)); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_MapTable::ReplicateLastEntry () + { + + uint16 *table = fTable->Buffer_uint16 (); + + uint16 lastEntry = table [fCount]; + + for (uint32 index = fCount; index < 0x10000; index++) + { + table [index] = lastEntry; + } + + } + +/*****************************************************************************/ + +void dng_opcode_MapTable::PutData (dng_stream &stream) const + { + + stream.Put_uint32 (dng_area_spec::kDataSize + 4 + fCount * 2); + + fAreaSpec.PutData (stream); + + stream.Put_uint32 (fCount); + + uint16 *table = fTable->Buffer_uint16 (); + + for (uint32 index = 0; index < fCount; index++) + { + stream.Put_uint16 (table [index]); + } + + } + +/*****************************************************************************/ + +uint32 dng_opcode_MapTable::BufferPixelType (uint32 /* imagePixelType */) + { + + return ttShort; + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_MapTable::ModifiedBounds (const dng_rect &imageBounds) + { + + return fAreaSpec.Overlap (imageBounds); + + } + +/*****************************************************************************/ + +void dng_opcode_MapTable::ProcessArea (dng_negative & /* negative */, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + DoMapArea16 (buffer.DirtyPixel_uint16 (overlap.t, overlap.l, plane), + 1, + (overlap.H () + fAreaSpec.RowPitch () - 1) / fAreaSpec.RowPitch (), + (overlap.W () + fAreaSpec.ColPitch () - 1) / fAreaSpec.ColPitch (), + 0, + fAreaSpec.RowPitch () * buffer.RowStep (), + fAreaSpec.ColPitch (), + fTable->Buffer_uint16 ()); + + } + + } + + } + +/*****************************************************************************/ + +dng_opcode_MapPolynomial::dng_opcode_MapPolynomial (const dng_area_spec &areaSpec, + uint32 degree, + const real64 *coefficient) + + : dng_inplace_opcode (dngOpcode_MapPolynomial, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + , fDegree (degree) + + { + + for (uint32 j = 0; j <= kMaxDegree; j++) + { + + if (j <= fDegree) + { + fCoefficient [j] = coefficient [j]; + } + + else + { + fCoefficient [j] = 0.0; + } + + } + + // Reduce degree if possible. + + while (fDegree > 0 && fCoefficient [fDegree] == 0.0) + { + fDegree--; + } + + } + +/*****************************************************************************/ + +dng_opcode_MapPolynomial::dng_opcode_MapPolynomial (dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_MapPolynomial, + stream, + "MapPolynomial") + + , fAreaSpec () + , fDegree (0) + + { + + uint32 dataSize = stream.Get_uint32 (); + + fAreaSpec.GetData (stream); + + fDegree = stream.Get_uint32 (); + + if (dataSize != dng_area_spec::kDataSize + 4 + (fDegree + 1) * 8) + { + ThrowBadFormat (); + } + + if (fDegree > kMaxDegree) + { + ThrowBadFormat (); + } + + for (uint32 j = 0; j <= kMaxDegree; j++) + { + + if (j <= fDegree) + { + fCoefficient [j] = stream.Get_real64 (); + } + else + { + fCoefficient [j] = 0.0; + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + for (uint32 k = 0; k <= fDegree; k++) + { + printf (" Coefficient [%u] = %f\n", (unsigned) k, fCoefficient [k]); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_MapPolynomial::PutData (dng_stream &stream) const + { + + stream.Put_uint32 (dng_area_spec::kDataSize + 4 + (fDegree + 1) * 8); + + fAreaSpec.PutData (stream); + + stream.Put_uint32 (fDegree); + + for (uint32 j = 0; j <= fDegree; j++) + { + stream.Put_real64 (fCoefficient [j]); + } + + } + +/*****************************************************************************/ + +uint32 dng_opcode_MapPolynomial::BufferPixelType (uint32 imagePixelType) + { + + // If we are operating on the stage 1 image, then we need + // to adjust the coefficients to convert from the image + // values to the 32-bit floating point values that this + // opcode operates on. + + // If we are operating on the stage 2 or 3 image, the logical + // range of the image is already 0.0 to 1.0, so we don't + // need to adjust the values. + + real64 scale32 = 1.0; + + if (Stage () == 1) + { + + switch (imagePixelType) + { + + case ttFloat: + break; + + case ttShort: + { + scale32 = (real64) 0xFFFF; + break; + } + + case ttLong: + { + scale32 = (real64) 0xFFFFFFFF; + break; + } + + default: + ThrowBadFormat (); + + } + + } + + real64 factor32 = 1.0 / scale32; + + for (uint32 j = 0; j <= kMaxDegree; j++) + { + + fCoefficient32 [j] = (real32) (fCoefficient [j] * factor32); + + factor32 *= scale32; + + } + + return ttFloat; + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_MapPolynomial::ModifiedBounds (const dng_rect &imageBounds) + { + + return fAreaSpec.Overlap (imageBounds); + + } + +/*****************************************************************************/ + +void dng_opcode_MapPolynomial::ProcessArea (dng_negative & /* negative */, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + uint32 cols = overlap.W (); + + uint32 colPitch = fAreaSpec.ColPitch (); + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + for (int32 row = overlap.t; row < overlap.b; row += fAreaSpec.RowPitch ()) + { + + real32 *dPtr = buffer.DirtyPixel_real32 (row, overlap.l, plane); + + switch (fDegree) + { + + case 0: + { + + real32 y = Pin_real32 (0.0f, + fCoefficient32 [0], + 1.0f); + + for (uint32 col = 0; col < cols; col += colPitch) + { + + dPtr [col] = y; + + } + + break; + + } + + case 1: + { + + real32 c0 = fCoefficient32 [0]; + real32 c1 = fCoefficient32 [1]; + + if (c0 == 0.0f) + { + + if (c1 > 0.0f) + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y = c1 * x; + + dPtr [col] = Min_real32 (y, 1.0f); + + } + + } + + else + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + dPtr [col] = 0.0f; + + } + + } + + } + + else + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y = c0 + + c1 * x; + + dPtr [col] = Pin_real32 (0.0f, y, 1.0f); + + } + + } + + break; + + } + + case 2: + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y = fCoefficient32 [0] + x * + (fCoefficient32 [1] + x * + (fCoefficient32 [2])); + + dPtr [col] = Pin_real32 (0.0f, y, 1.0f); + + } + + break; + + } + + case 3: + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y = fCoefficient32 [0] + x * + (fCoefficient32 [1] + x * + (fCoefficient32 [2] + x * + (fCoefficient32 [3]))); + + dPtr [col] = Pin_real32 (0.0f, y, 1.0f); + + } + + break; + + } + + case 4: + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y = fCoefficient32 [0] + x * + (fCoefficient32 [1] + x * + (fCoefficient32 [2] + x * + (fCoefficient32 [3] + x * + (fCoefficient32 [4])))); + + dPtr [col] = Pin_real32 (0.0f, y, 1.0f); + + } + + break; + + } + + default: + { + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y = fCoefficient32 [0]; + + real32 xx = x; + + for (uint32 j = 1; j <= fDegree; j++) + { + + y += fCoefficient32 [j] * xx; + + xx *= x; + + } + + dPtr [col] = Pin_real32 (0.0f, y, 1.0f); + + } + + } + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_opcode_DeltaPerRow::dng_opcode_DeltaPerRow (const dng_area_spec &areaSpec, + AutoPtr &table) + + : dng_inplace_opcode (dngOpcode_DeltaPerRow, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + , fTable () + , fScale (1.0f) + + { + + fTable.Reset (table.Release ()); + + } + +/*****************************************************************************/ + +dng_opcode_DeltaPerRow::dng_opcode_DeltaPerRow (dng_host &host, + dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_DeltaPerRow, + stream, + "DeltaPerRow") + + , fAreaSpec () + , fTable () + , fScale (1.0f) + + { + + uint32 dataSize = stream.Get_uint32 (); + + fAreaSpec.GetData (stream); + + uint32 deltas = (fAreaSpec.Area ().H () + + fAreaSpec.RowPitch () - 1) / + fAreaSpec.RowPitch (); + + if (deltas != stream.Get_uint32 ()) + { + ThrowBadFormat (); + } + + if (dataSize != dng_area_spec::kDataSize + 4 + deltas * 4) + { + ThrowBadFormat (); + } + + fTable.Reset (host.Allocate (deltas * (uint32) sizeof (real32))); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < deltas; j++) + { + table [j] = stream.Get_real32 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Count: %u\n", (unsigned) deltas); + + for (uint32 k = 0; k < deltas && k < gDumpLineLimit; k++) + { + printf (" Delta [%u] = %f\n", (unsigned) k, table [k]); + } + + if (deltas > gDumpLineLimit) + { + printf (" ... %u deltas skipped\n", (unsigned) (deltas - gDumpLineLimit)); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_DeltaPerRow::PutData (dng_stream &stream) const + { + + uint32 deltas = (fAreaSpec.Area ().H () + + fAreaSpec.RowPitch () - 1) / + fAreaSpec.RowPitch (); + + stream.Put_uint32 (dng_area_spec::kDataSize + 4 + deltas * 4); + + fAreaSpec.PutData (stream); + + stream.Put_uint32 (deltas); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < deltas; j++) + { + stream.Put_real32 (table [j]); + } + + } + +/*****************************************************************************/ + +uint32 dng_opcode_DeltaPerRow::BufferPixelType (uint32 imagePixelType) + { + + real64 scale32 = 1.0; + + switch (imagePixelType) + { + + case ttFloat: + break; + + case ttShort: + { + scale32 = (real64) 0xFFFF; + break; + } + + case ttLong: + { + scale32 = (real64) 0xFFFFFFFF; + break; + } + + default: + ThrowBadFormat (); + + } + + fScale = (real32) (1.0 / scale32); + + return ttFloat; + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_DeltaPerRow::ModifiedBounds (const dng_rect &imageBounds) + { + + return fAreaSpec.Overlap (imageBounds); + + } + +/*****************************************************************************/ + +void dng_opcode_DeltaPerRow::ProcessArea (dng_negative & /* negative */, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + uint32 cols = overlap.W (); + + uint32 colPitch = fAreaSpec.ColPitch (); + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + const real32 *table = fTable->Buffer_real32 () + + ((overlap.t - fAreaSpec.Area ().t) / + fAreaSpec.RowPitch ()); + + for (int32 row = overlap.t; row < overlap.b; row += fAreaSpec.RowPitch ()) + { + + real32 rowDelta = *(table++) * fScale; + + real32 *dPtr = buffer.DirtyPixel_real32 (row, overlap.l, plane); + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y = x + rowDelta; + + dPtr [col] = Pin_real32 (0.0f, y, 1.0f); + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_opcode_DeltaPerColumn::dng_opcode_DeltaPerColumn (const dng_area_spec &areaSpec, + AutoPtr &table) + + : dng_inplace_opcode (dngOpcode_DeltaPerColumn, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + , fTable () + , fScale (1.0f) + + { + + fTable.Reset (table.Release ()); + + } + +/*****************************************************************************/ + +dng_opcode_DeltaPerColumn::dng_opcode_DeltaPerColumn (dng_host &host, + dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_DeltaPerColumn, + stream, + "DeltaPerColumn") + + , fAreaSpec () + , fTable () + , fScale (1.0f) + + { + + uint32 dataSize = stream.Get_uint32 (); + + fAreaSpec.GetData (stream); + + uint32 deltas = (fAreaSpec.Area ().W () + + fAreaSpec.ColPitch () - 1) / + fAreaSpec.ColPitch (); + + if (deltas != stream.Get_uint32 ()) + { + ThrowBadFormat (); + } + + if (dataSize != dng_area_spec::kDataSize + 4 + deltas * 4) + { + ThrowBadFormat (); + } + + fTable.Reset (host.Allocate (deltas * (uint32) sizeof (real32))); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < deltas; j++) + { + table [j] = stream.Get_real32 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Count: %u\n", (unsigned) deltas); + + for (uint32 k = 0; k < deltas && k < gDumpLineLimit; k++) + { + printf (" Delta [%u] = %f\n", (unsigned) k, table [k]); + } + + if (deltas > gDumpLineLimit) + { + printf (" ... %u deltas skipped\n", (unsigned) (deltas - gDumpLineLimit)); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_DeltaPerColumn::PutData (dng_stream &stream) const + { + + uint32 deltas = (fAreaSpec.Area ().W () + + fAreaSpec.ColPitch () - 1) / + fAreaSpec.ColPitch (); + + stream.Put_uint32 (dng_area_spec::kDataSize + 4 + deltas * 4); + + fAreaSpec.PutData (stream); + + stream.Put_uint32 (deltas); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < deltas; j++) + { + stream.Put_real32 (table [j]); + } + + } + +/*****************************************************************************/ + +uint32 dng_opcode_DeltaPerColumn::BufferPixelType (uint32 imagePixelType) + { + + real64 scale32 = 1.0; + + switch (imagePixelType) + { + + case ttFloat: + break; + + case ttShort: + { + scale32 = (real64) 0xFFFF; + break; + } + + case ttLong: + { + scale32 = (real64) 0xFFFFFFFF; + break; + } + + default: + ThrowBadFormat (); + + } + + fScale = (real32) (1.0 / scale32); + + return ttFloat; + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_DeltaPerColumn::ModifiedBounds (const dng_rect &imageBounds) + { + + return fAreaSpec.Overlap (imageBounds); + + } + +/*****************************************************************************/ + +void dng_opcode_DeltaPerColumn::ProcessArea (dng_negative & /* negative */, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + uint32 rows = (overlap.H () + fAreaSpec.RowPitch () - 1) / + fAreaSpec.RowPitch (); + + int32 rowStep = buffer.RowStep () * fAreaSpec.RowPitch (); + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + const real32 *table = fTable->Buffer_real32 () + + ((overlap.l - fAreaSpec.Area ().l) / + fAreaSpec.ColPitch ()); + + for (int32 col = overlap.l; col < overlap.r; col += fAreaSpec.ColPitch ()) + { + + real32 colDelta = *(table++) * fScale; + + real32 *dPtr = buffer.DirtyPixel_real32 (overlap.t, col, plane); + + for (uint32 row = 0; row < rows; row++) + { + + real32 x = dPtr [0]; + + real32 y = x + colDelta; + + dPtr [0] = Pin_real32 (0.0f, y, 1.0f); + + dPtr += rowStep; + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_opcode_ScalePerRow::dng_opcode_ScalePerRow (const dng_area_spec &areaSpec, + AutoPtr &table) + + : dng_inplace_opcode (dngOpcode_ScalePerRow, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + , fTable () + + { + + fTable.Reset (table.Release ()); + + } + +/*****************************************************************************/ + +dng_opcode_ScalePerRow::dng_opcode_ScalePerRow (dng_host &host, + dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_ScalePerRow, + stream, + "ScalePerRow") + + , fAreaSpec () + , fTable () + + { + + uint32 dataSize = stream.Get_uint32 (); + + fAreaSpec.GetData (stream); + + uint32 scales = (fAreaSpec.Area ().H () + + fAreaSpec.RowPitch () - 1) / + fAreaSpec.RowPitch (); + + if (scales != stream.Get_uint32 ()) + { + ThrowBadFormat (); + } + + if (dataSize != dng_area_spec::kDataSize + 4 + scales * 4) + { + ThrowBadFormat (); + } + + fTable.Reset (host.Allocate (scales * (uint32) sizeof (real32))); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < scales; j++) + { + table [j] = stream.Get_real32 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Count: %u\n", (unsigned) scales); + + for (uint32 k = 0; k < scales && k < gDumpLineLimit; k++) + { + printf (" Scale [%u] = %f\n", (unsigned) k, table [k]); + } + + if (scales > gDumpLineLimit) + { + printf (" ... %u scales skipped\n", (unsigned) (scales - gDumpLineLimit)); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_ScalePerRow::PutData (dng_stream &stream) const + { + + uint32 scales = (fAreaSpec.Area ().H () + + fAreaSpec.RowPitch () - 1) / + fAreaSpec.RowPitch (); + + stream.Put_uint32 (dng_area_spec::kDataSize + 4 + scales * 4); + + fAreaSpec.PutData (stream); + + stream.Put_uint32 (scales); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < scales; j++) + { + stream.Put_real32 (table [j]); + } + + } + +/*****************************************************************************/ + +uint32 dng_opcode_ScalePerRow::BufferPixelType (uint32 /* imagePixelType */) + { + + return ttFloat; + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_ScalePerRow::ModifiedBounds (const dng_rect &imageBounds) + { + + return fAreaSpec.Overlap (imageBounds); + + } + +/*****************************************************************************/ + +void dng_opcode_ScalePerRow::ProcessArea (dng_negative & /* negative */, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + uint32 cols = overlap.W (); + + uint32 colPitch = fAreaSpec.ColPitch (); + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + const real32 *table = fTable->Buffer_real32 () + + ((overlap.t - fAreaSpec.Area ().t) / + fAreaSpec.RowPitch ()); + + for (int32 row = overlap.t; row < overlap.b; row += fAreaSpec.RowPitch ()) + { + + real32 rowScale = *(table++); + + real32 *dPtr = buffer.DirtyPixel_real32 (row, overlap.l, plane); + + for (uint32 col = 0; col < cols; col += colPitch) + { + + real32 x = dPtr [col]; + + real32 y = x * rowScale; + + dPtr [col] = Min_real32 (y, 1.0f); + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_opcode_ScalePerColumn::dng_opcode_ScalePerColumn (const dng_area_spec &areaSpec, + AutoPtr &table) + + : dng_inplace_opcode (dngOpcode_ScalePerColumn, + dngVersion_1_3_0_0, + kFlag_None) + + , fAreaSpec (areaSpec) + , fTable () + + { + + fTable.Reset (table.Release ()); + + } + +/*****************************************************************************/ + +dng_opcode_ScalePerColumn::dng_opcode_ScalePerColumn (dng_host &host, + dng_stream &stream) + + : dng_inplace_opcode (dngOpcode_ScalePerColumn, + stream, + "ScalePerColumn") + + , fAreaSpec () + , fTable () + + { + + uint32 dataSize = stream.Get_uint32 (); + + fAreaSpec.GetData (stream); + + uint32 scales = (fAreaSpec.Area ().W () + + fAreaSpec.ColPitch () - 1) / + fAreaSpec.ColPitch (); + + if (scales != stream.Get_uint32 ()) + { + ThrowBadFormat (); + } + + if (dataSize != dng_area_spec::kDataSize + 4 + scales * 4) + { + ThrowBadFormat (); + } + + fTable.Reset (host.Allocate (scales * (uint32) sizeof (real32))); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < scales; j++) + { + table [j] = stream.Get_real32 (); + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("Count: %u\n", (unsigned) scales); + + for (uint32 k = 0; k < scales && k < gDumpLineLimit; k++) + { + printf (" Scale [%u] = %f\n", (unsigned) k, table [k]); + } + + if (scales > gDumpLineLimit) + { + printf (" ... %u deltas skipped\n", (unsigned) (scales - gDumpLineLimit)); + } + + } + + #endif + + } + +/*****************************************************************************/ + +void dng_opcode_ScalePerColumn::PutData (dng_stream &stream) const + { + + uint32 scales = (fAreaSpec.Area ().W () + + fAreaSpec.ColPitch () - 1) / + fAreaSpec.ColPitch (); + + stream.Put_uint32 (dng_area_spec::kDataSize + 4 + scales * 4); + + fAreaSpec.PutData (stream); + + stream.Put_uint32 (scales); + + real32 *table = fTable->Buffer_real32 (); + + for (uint32 j = 0; j < scales; j++) + { + stream.Put_real32 (table [j]); + } + + } + +/*****************************************************************************/ + +uint32 dng_opcode_ScalePerColumn::BufferPixelType (uint32 /* imagePixelType */) + { + + return ttFloat; + + } + +/*****************************************************************************/ + +dng_rect dng_opcode_ScalePerColumn::ModifiedBounds (const dng_rect &imageBounds) + { + + return fAreaSpec.Overlap (imageBounds); + + } + +/*****************************************************************************/ + +void dng_opcode_ScalePerColumn::ProcessArea (dng_negative & /* negative */, + uint32 /* threadIndex */, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + + dng_rect overlap = fAreaSpec.Overlap (dstArea); + + if (overlap.NotEmpty ()) + { + + uint32 rows = (overlap.H () + fAreaSpec.RowPitch () - 1) / + fAreaSpec.RowPitch (); + + int32 rowStep = buffer.RowStep () * fAreaSpec.RowPitch (); + + for (uint32 plane = fAreaSpec.Plane (); + plane < fAreaSpec.Plane () + fAreaSpec.Planes () && + plane < buffer.Planes (); + plane++) + { + + const real32 *table = fTable->Buffer_real32 () + + ((overlap.l - fAreaSpec.Area ().l) / + fAreaSpec.ColPitch ()); + + for (int32 col = overlap.l; col < overlap.r; col += fAreaSpec.ColPitch ()) + { + + real32 colScale = *(table++); + + real32 *dPtr = buffer.DirtyPixel_real32 (overlap.t, col, plane); + + for (uint32 row = 0; row < rows; row++) + { + + real32 x = dPtr [0]; + + real32 y = x * colScale; + + dPtr [0] = Min_real32 (y, 1.0f); + + dPtr += rowStep; + + } + + } + + } + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_misc_opcodes.h b/source/lib/dng_sdk/dng_misc_opcodes.h new file mode 100644 index 0000000..a211a9a --- /dev/null +++ b/source/lib/dng_sdk/dng_misc_opcodes.h @@ -0,0 +1,417 @@ +/*****************************************************************************/ +// Copyright 2008-2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_misc_opcodes.h#2 $ */ +/* $DateTime: 2012/08/02 06:09:06 $ */ +/* $Change: 841096 $ */ +/* $Author: erichan $ */ + +/** \file + * Miscellaneous DNG opcodes. + */ + +/*****************************************************************************/ + +#ifndef __dng_misc_opcodes__ +#define __dng_misc_opcodes__ + +/*****************************************************************************/ + +#include "dng_opcodes.h" + +/*****************************************************************************/ + +/// \brief Opcode to trim image to a specified rectangle. + +class dng_opcode_TrimBounds: public dng_opcode + { + + private: + + dng_rect fBounds; + + public: + + /// Create opcode to trim image to the specified bounds. + + dng_opcode_TrimBounds (const dng_rect &bounds); + + dng_opcode_TrimBounds (dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + }; + +/*****************************************************************************/ + +/// \brief A class to describe an area of an image, including a pixel subrectangle, +/// plane range, and row/column pitch (e.g., for mosaic images). Useful for +/// specifying opcodes that only apply to specific color planes or pixel types (e.g., +/// only one of the two green Bayer pixels). + +class dng_area_spec + { + + public: + + enum + { + kDataSize = 32 + }; + + private: + + dng_rect fArea; + + uint32 fPlane; + uint32 fPlanes; + + uint32 fRowPitch; + uint32 fColPitch; + + public: + + /// Create an empty area. + + dng_area_spec (const dng_rect &area = dng_rect (), + uint32 plane = 0, + uint32 planes = 1, + uint32 rowPitch = 1, + uint32 colPitch = 1) + + : fArea (area) + , fPlane (plane) + , fPlanes (planes) + , fRowPitch (rowPitch) + , fColPitch (colPitch) + + { + } + + /// The pixel area. + + const dng_rect & Area () const + { + return fArea; + } + + /// The first plane. + + const uint32 Plane () const + { + return fPlane; + } + + /// The total number of planes. + + const uint32 Planes () const + { + return fPlanes; + } + + /// The row pitch (i.e., stride). A pitch of 1 means all rows. + + const uint32 RowPitch () const + { + return fRowPitch; + } + + /// The column pitch (i.e., stride). A pitch of 1 means all columns. + + const uint32 ColPitch () const + { + return fColPitch; + } + + /// Read area data from the specified stream. + + void GetData (dng_stream &stream); + + /// Write area data to the specified stream. + + void PutData (dng_stream &stream) const; + + /// Compute and return pixel area overlap (i.e., intersection) between this + /// area and the specified tile. + + dng_rect Overlap (const dng_rect &tile) const; + + }; + +/*****************************************************************************/ + +/// \brief An opcode to apply a 1D function (represented as a 16-bit table) to an +/// image area. + +class dng_opcode_MapTable: public dng_inplace_opcode + { + + private: + + dng_area_spec fAreaSpec; + + AutoPtr fTable; + + uint32 fCount; + + public: + + /// Create a MapTable opcode with the specified area, table, and number of + /// table entries. + + dng_opcode_MapTable (dng_host &host, + const dng_area_spec &areaSpec, + const uint16 *table, + uint32 count = 0x10000); + + dng_opcode_MapTable (dng_host &host, + dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 imagePixelType); + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + private: + + void ReplicateLastEntry (); + + }; + +/*****************************************************************************/ + +/// \brief An opcode to apply a 1D function (represented as a polynomial) to an +/// image area. + +class dng_opcode_MapPolynomial: public dng_inplace_opcode + { + + public: + + enum + { + kMaxDegree = 8 + }; + + private: + + dng_area_spec fAreaSpec; + + uint32 fDegree; + + real64 fCoefficient [kMaxDegree + 1]; + + real32 fCoefficient32 [kMaxDegree + 1]; + + public: + + /// Create a MapPolynomial opcode with the specified area, polynomial + /// degree, and polynomial coefficients. The function that will be + /// applied to each pixel x is: + /// + /// f (x) = coefficient [0] + ((x * coefficient [1]) + + /// (x^2 * coefficient [2]) + + /// (x^3 * coefficient [3]) + + /// (x^4 * coefficient [4]) ... + + dng_opcode_MapPolynomial (const dng_area_spec &areaSpec, + uint32 degree, + const real64 *coefficient); + + dng_opcode_MapPolynomial (dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 imagePixelType); + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + }; + +/*****************************************************************************/ + +/// \brief An opcode to apply a delta (i.e., offset) that varies per row. Within +/// a row, the same delta value is applied to all specified pixels. + +class dng_opcode_DeltaPerRow: public dng_inplace_opcode + { + + private: + + dng_area_spec fAreaSpec; + + AutoPtr fTable; + + real32 fScale; + + public: + + /// Create a DeltaPerRow opcode with the specified area and row deltas + /// (specified as a table of 32-bit floats). + + dng_opcode_DeltaPerRow (const dng_area_spec &areaSpec, + AutoPtr &table); + + dng_opcode_DeltaPerRow (dng_host &host, + dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 imagePixelType); + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + }; + +/*****************************************************************************/ + +/// \brief An opcode to apply a delta (i.e., offset) that varies per column. +/// Within a column, the same delta value is applied to all specified pixels. + +class dng_opcode_DeltaPerColumn: public dng_inplace_opcode + { + + private: + + dng_area_spec fAreaSpec; + + AutoPtr fTable; + + real32 fScale; + + public: + + /// Create a DeltaPerColumn opcode with the specified area and column + /// deltas (specified as a table of 32-bit floats). + + dng_opcode_DeltaPerColumn (const dng_area_spec &areaSpec, + AutoPtr &table); + + dng_opcode_DeltaPerColumn (dng_host &host, + dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 imagePixelType); + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + }; + +/*****************************************************************************/ + +/// \brief An opcode to apply a scale factor that varies per row. Within a row, +/// the same scale factor is applied to all specified pixels. + +class dng_opcode_ScalePerRow: public dng_inplace_opcode + { + + private: + + dng_area_spec fAreaSpec; + + AutoPtr fTable; + + public: + + /// Create a ScalePerRow opcode with the specified area and row scale + /// factors (specified as a table of 32-bit floats). + + dng_opcode_ScalePerRow (const dng_area_spec &areaSpec, + AutoPtr &table); + + dng_opcode_ScalePerRow (dng_host &host, + dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 imagePixelType); + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + }; + +/*****************************************************************************/ + +/// \brief An opcode to apply a scale factor that varies per column. Within a +/// column, the same scale factor is applied to all specified pixels. + +class dng_opcode_ScalePerColumn: public dng_inplace_opcode + { + + private: + + dng_area_spec fAreaSpec; + + AutoPtr fTable; + + public: + + /// Create a ScalePerColumn opcode with the specified area and column + /// scale factors (specified as a table of 32-bit floats). + + dng_opcode_ScalePerColumn (const dng_area_spec &areaSpec, + AutoPtr &table); + + dng_opcode_ScalePerColumn (dng_host &host, + dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual uint32 BufferPixelType (uint32 imagePixelType); + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds); + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_mosaic_info.cpp b/source/lib/dng_sdk/dng_mosaic_info.cpp new file mode 100644 index 0000000..a98a59f --- /dev/null +++ b/source/lib/dng_sdk/dng_mosaic_info.cpp @@ -0,0 +1,1991 @@ +/*****************************************************************************/ +// Copyright 2006-2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_mosaic_info.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_mosaic_info.h" + +#include "dng_area_task.h" +#include "dng_assertions.h" +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_filter_task.h" +#include "dng_host.h" +#include "dng_ifd.h" +#include "dng_image.h" +#include "dng_info.h" +#include "dng_negative.h" +#include "dng_pixel_buffer.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_tile_iterator.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +// A interpolation kernel for a single pixel of a single plane. + +class dng_bilinear_kernel + { + + public: + + enum + { + kMaxCount = 8 + }; + + uint32 fCount; + + dng_point fDelta [kMaxCount]; + + real32 fWeight32 [kMaxCount]; + uint16 fWeight16 [kMaxCount]; + + int32 fOffset [kMaxCount]; + + public: + + dng_bilinear_kernel () + : fCount (0) + { + } + + void Add (const dng_point &delta, + real32 weight); + + void Finalize (const dng_point &scale, + uint32 patRow, + uint32 patCol, + int32 rowStep, + int32 colStep); + + }; + +/*****************************************************************************/ + +void dng_bilinear_kernel::Add (const dng_point &delta, + real32 weight) + { + + // Don't add zero weight elements. + + if (weight <= 0.0f) + { + return; + } + + // If the delta already matches an existing element, just combine the + // weights. + + for (uint32 j = 0; j < fCount; j++) + { + + if (fDelta [j] == delta) + { + + fWeight32 [j] += weight; + + return; + + } + + } + + // Add element to list. + + DNG_ASSERT (fCount < kMaxCount, "Too many kernel entries") + + fDelta [fCount] = delta; + fWeight32 [fCount] = weight; + + fCount++; + + } + +/*****************************************************************************/ + +void dng_bilinear_kernel::Finalize (const dng_point &scale, + uint32 patRow, + uint32 patCol, + int32 rowStep, + int32 colStep) + { + + uint32 j; + + // Adjust deltas to compensate for interpolation upscaling. + + for (j = 0; j < fCount; j++) + { + + dng_point &delta = fDelta [j]; + + if (scale.v == 2) + { + + delta.v = (delta.v + (int32) (patRow & 1)) >> 1; + + } + + if (scale.h == 2) + { + + delta.h = (delta.h + (int32) (patCol & 1)) >> 1; + + } + + } + + // Sort entries into row-column scan order. + + while (true) + { + + bool didSwap = false; + + for (j = 1; j < fCount; j++) + { + + dng_point &delta0 = fDelta [j - 1]; + dng_point &delta1 = fDelta [j ]; + + if (delta0.v > delta1.v || + (delta0.v == delta1.v && + delta0.h > delta1.h)) + { + + didSwap = true; + + dng_point tempDelta = delta0; + + delta0 = delta1; + delta1 = tempDelta; + + real32 tempWeight = fWeight32 [j - 1]; + + fWeight32 [j - 1] = fWeight32 [j]; + fWeight32 [j ] = tempWeight; + + } + + } + + if (!didSwap) + { + break; + } + + } + + // Calculate offsets. + + for (j = 0; j < fCount; j++) + { + + fOffset [j] = rowStep * fDelta [j].v + + colStep * fDelta [j].h; + + } + + // Calculate 16-bit weights. + + uint16 total = 0; + uint32 biggest = 0; + + for (j = 0; j < fCount; j++) + { + + // Round weights to 8 fractional bits. + + fWeight16 [j] = (uint16) Round_uint32 (fWeight32 [j] * 256.0); + + // Keep track of total of weights. + + total += fWeight16 [j]; + + // Keep track of which weight is biggest. + + if (fWeight16 [biggest] < fWeight16 [j]) + { + + biggest = j; + + } + + } + + // Adjust largest entry so total of weights is exactly 256. + + fWeight16 [biggest] += (256 - total); + + // Recompute the floating point weights from the rounded integer weights + // so results match more closely. + + for (j = 0; j < fCount; j++) + { + + fWeight32 [j] = fWeight16 [j] * (1.0f / 256.0f); + + } + + } + +/*****************************************************************************/ + +class dng_bilinear_pattern + { + + public: + + enum + { + kMaxPattern = kMaxCFAPattern * 2 + }; + + dng_point fScale; + + uint32 fPatRows; + uint32 fPatCols; + + dng_bilinear_kernel fKernel [kMaxPattern] + [kMaxPattern]; + + uint32 fCounts [kMaxPattern] + [kMaxPattern]; + + int32 *fOffsets [kMaxPattern] + [kMaxPattern]; + + uint16 *fWeights16 [kMaxPattern] + [kMaxPattern]; + + real32 *fWeights32 [kMaxPattern] + [kMaxPattern]; + + public: + + dng_bilinear_pattern () + + : fScale () + , fPatRows (0) + , fPatCols (0) + + { + } + + private: + + uint32 DeltaRow (uint32 row, int32 delta) + { + return (row + fPatRows + delta) % fPatRows; + } + + uint32 DeltaCol (uint32 col, int32 delta) + { + return (col + fPatCols + delta) % fPatCols; + } + + real32 LinearWeight1 (int32 d1, int32 d2) + { + if (d1 == d2) + return 1.0f; + else + return d2 / (real32) (d2 - d1); + } + + real32 LinearWeight2 (int32 d1, int32 d2) + { + if (d1 == d2) + return 0.0f; + else + return -d1 / (real32) (d2 - d1); + } + + public: + + void Calculate (const dng_mosaic_info &info, + uint32 dstPlane, + int32 rowStep, + int32 colStep); + + }; + +/*****************************************************************************/ + +void dng_bilinear_pattern::Calculate (const dng_mosaic_info &info, + uint32 dstPlane, + int32 rowStep, + int32 colStep) + { + + uint32 j; + uint32 k; + uint32 patRow; + uint32 patCol; + + // Find destination pattern size. + + fScale = info.FullScale (); + + fPatRows = info.fCFAPatternSize.v * fScale.v; + fPatCols = info.fCFAPatternSize.h * fScale.h; + + // See if we need to scale up just while computing the kernels. + + dng_point tempScale (1, 1); + + if (info.fCFALayout >= 6) + { + + tempScale = dng_point (2, 2); + + fPatRows *= tempScale.v; + fPatCols *= tempScale.h; + + } + + // Find a boolean map for this plane color and layout. + + bool map [kMaxPattern] + [kMaxPattern]; + + uint8 planeColor = info.fCFAPlaneColor [dstPlane]; + + switch (info.fCFALayout) + { + + case 1: // Rectangular (or square) layout + { + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + map [j] [k] = (info.fCFAPattern [j] [k] == planeColor); + + } + + } + + break; + + } + + // Note that when the descriptions of the staggered patterns refer to even rows or + // columns, this mean the second, fourth, etc. (i.e. using one-based numbering). + // This needs to be clarified in the DNG specification. + + case 2: // Staggered layout A: even (1-based) columns are offset down by 1/2 row + { + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + if ((j & 1) != (k & 1)) + { + + map [j] [k] = false; + + } + + else + { + + map [j] [k] = (info.fCFAPattern [j >> 1] [k] == planeColor); + + } + + } + + } + + break; + + } + + case 3: // Staggered layout B: even (1-based) columns are offset up by 1/2 row + { + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + if ((j & 1) == (k & 1)) + { + + map [j] [k] = false; + + } + + else + { + + map [j] [k] = (info.fCFAPattern [j >> 1] [k] == planeColor); + + } + + } + + } + + break; + + } + + case 4: // Staggered layout C: even (1-based) rows are offset right by 1/2 column + { + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + if ((j & 1) != (k & 1)) + { + + map [j] [k] = false; + + } + + else + { + + map [j] [k] = (info.fCFAPattern [j] [k >> 1] == planeColor); + + } + + } + + } + + break; + + } + + case 5: // Staggered layout D: even (1-based) rows are offset left by 1/2 column + { + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + if ((j & 1) == (k & 1)) + { + + map [j] [k] = false; + + } + + else + { + + map [j] [k] = (info.fCFAPattern [j] [k >> 1] == planeColor); + + } + + } + + } + + break; + + } + + case 6: // Staggered layout E: even rows are offset up by 1/2 row, even columns are offset left by 1/2 column + case 7: // Staggered layout F: even rows are offset up by 1/2 row, even columns are offset right by 1/2 column + case 8: // Staggered layout G: even rows are offset down by 1/2 row, even columns are offset left by 1/2 column + case 9: // Staggered layout H: even rows are offset down by 1/2 row, even columns are offset right by 1/2 column + { + + uint32 eRow = (info.fCFALayout == 6 || + info.fCFALayout == 7) ? 1 : 3; + + uint32 eCol = (info.fCFALayout == 6 || + info.fCFALayout == 8) ? 1 : 3; + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + uint32 jj = j & 3; + uint32 kk = k & 3; + + if ((jj != 0 && jj != eRow) || + (kk != 0 && kk != eCol)) + { + + map [j] [k] = false; + + } + + else + { + + map [j] [k] = (info.fCFAPattern [((j >> 1) & ~1) + Min_uint32 (jj, 1)] + [((k >> 1) & ~1) + Min_uint32 (kk, 1)] == planeColor); + + } + + } + + } + + break; + + } + + default: + ThrowProgramError (); + + } + + // Find projections of maps. + + bool mapH [kMaxPattern]; + bool mapV [kMaxPattern]; + + for (j = 0; j < kMaxPattern; j++) + { + + mapH [j] = false; + mapV [j] = false; + + } + + for (j = 0; j < fPatRows; j++) + { + + for (k = 0; k < fPatCols; k++) + { + + if (map [j] [k]) + { + + mapV [j] = true; + mapH [k] = true; + + } + + } + + } + + // Find kernel for each patten entry. + + for (patRow = 0; patRow < fPatRows; patRow += tempScale.v) + { + + for (patCol = 0; patCol < fPatCols; patCol += tempScale.h) + { + + dng_bilinear_kernel &kernel = fKernel [patRow] [patCol]; + + // Special case no interpolation case. + + if (map [patRow] [patCol]) + { + + kernel.Add (dng_point (0, 0), 1.0f); + + continue; + + } + + // Special case common patterns in 3 by 3 neighborhood. + + uint32 n = DeltaRow (patRow, -1); + uint32 s = DeltaRow (patRow, 1); + uint32 w = DeltaCol (patCol, -1); + uint32 e = DeltaCol (patCol, 1); + + bool mapNW = map [n] [w]; + bool mapN = map [n] [patCol]; + bool mapNE = map [n] [e]; + + bool mapW = map [patRow] [w]; + bool mapE = map [patRow] [e]; + + bool mapSW = map [s] [w]; + bool mapS = map [s] [patCol]; + bool mapSE = map [s] [e]; + + // All sides. + + if (mapN && mapS && mapW && mapW) + { + + kernel.Add (dng_point (-1, 0), 0.25f); + kernel.Add (dng_point ( 0, -1), 0.25f); + kernel.Add (dng_point ( 0, 1), 0.25f); + kernel.Add (dng_point ( 1, 0), 0.25f); + + continue; + + } + + // N & S. + + if (mapN && mapS) + { + + kernel.Add (dng_point (-1, 0), 0.5f); + kernel.Add (dng_point ( 1, 0), 0.5f); + + continue; + + } + + // E & W. + + if (mapW && mapE) + { + + kernel.Add (dng_point ( 0, -1), 0.5f); + kernel.Add (dng_point ( 0, 1), 0.5f); + + continue; + + } + + // N & SW & SE. + + if (mapN && mapSW && mapSE) + { + + kernel.Add (dng_point (-1, 0), 0.50f); + kernel.Add (dng_point ( 1, -1), 0.25f); + kernel.Add (dng_point ( 1, 1), 0.25f); + + continue; + + } + + // S & NW & NE. + + if (mapS && mapNW && mapNE) + { + + kernel.Add (dng_point (-1, -1), 0.25f); + kernel.Add (dng_point (-1, 1), 0.25f); + kernel.Add (dng_point ( 1, 0), 0.50f); + + continue; + + } + + // W & NE & SE. + + if (mapW && mapNE && mapSE) + { + + kernel.Add (dng_point (-1, 1), 0.25f); + kernel.Add (dng_point ( 0, -1), 0.50f); + kernel.Add (dng_point ( 1, 1), 0.25f); + + continue; + + } + + // E & NW & SW. + + if (mapE && mapNW && mapSW) + { + + kernel.Add (dng_point (-1, -1), 0.25f); + kernel.Add (dng_point ( 0, 1), 0.50f); + kernel.Add (dng_point ( 1, -1), 0.25f); + + continue; + + } + + // Four corners. + + if (mapNW && mapNE && mapSE && mapSW) + { + + kernel.Add (dng_point (-1, -1), 0.25f); + kernel.Add (dng_point (-1, 1), 0.25f); + kernel.Add (dng_point ( 1, -1), 0.25f); + kernel.Add (dng_point ( 1, 1), 0.25f); + + continue; + + } + + // NW & SE + + if (mapNW && mapSE) + { + + kernel.Add (dng_point (-1, -1), 0.50f); + kernel.Add (dng_point ( 1, 1), 0.50f); + + continue; + + } + + // NE & SW + + if (mapNE && mapSW) + { + + kernel.Add (dng_point (-1, 1), 0.50f); + kernel.Add (dng_point ( 1, -1), 0.50f); + + continue; + + } + + // Else use double-bilinear kernel. + + int32 dv1 = 0; + int32 dv2 = 0; + + while (!mapV [DeltaRow (patRow, dv1)]) + { + dv1--; + } + + while (!mapV [DeltaRow (patRow, dv2)]) + { + dv2++; + } + + real32 w1 = LinearWeight1 (dv1, dv2) * 0.5f; + real32 w2 = LinearWeight2 (dv1, dv2) * 0.5f; + + int32 v1 = DeltaRow (patRow, dv1); + int32 v2 = DeltaRow (patRow, dv2); + + int32 dh1 = 0; + int32 dh2 = 0; + + while (!map [v1] [DeltaCol (patCol, dh1)]) + { + dh1--; + } + + while (!map [v1] [DeltaCol (patCol, dh2)]) + { + dh2++; + } + + kernel.Add (dng_point (dv1, dh1), + LinearWeight1 (dh1, dh2) * w1); + + kernel.Add (dng_point (dv1, dh2), + LinearWeight2 (dh1, dh2) * w1); + + dh1 = 0; + dh2 = 0; + + while (!map [v2] [DeltaCol (patCol, dh1)]) + { + dh1--; + } + + while (!map [v2] [DeltaCol (patCol, dh2)]) + { + dh2++; + } + + kernel.Add (dng_point (dv2, dh1), + LinearWeight1 (dh1, dh2) * w2); + + kernel.Add (dng_point (dv2, dh2), + LinearWeight2 (dh1, dh2) * w2); + + dh1 = 0; + dh2 = 0; + + while (!mapH [DeltaCol (patCol, dh1)]) + { + dh1--; + } + + while (!mapH [DeltaCol (patCol, dh2)]) + { + dh2++; + } + + w1 = LinearWeight1 (dh1, dh2) * 0.5f; + w2 = LinearWeight2 (dh1, dh2) * 0.5f; + + int32 h1 = DeltaCol (patCol, dh1); + int32 h2 = DeltaCol (patCol, dh2); + + dv1 = 0; + dv2 = 0; + + while (!map [DeltaRow (patRow, dv1)] [h1]) + { + dv1--; + } + + while (!map [DeltaRow (patRow, dv2)] [h1]) + { + dv2++; + } + + kernel.Add (dng_point (dv1, dh1), + LinearWeight1 (dv1, dv2) * w1); + + kernel.Add (dng_point (dv2, dh1), + LinearWeight2 (dv1, dv2) * w1); + + dv1 = 0; + dv2 = 0; + + while (!map [DeltaRow (patRow, dv1)] [h2]) + { + dv1--; + } + + while (!map [DeltaRow (patRow, dv2)] [h2]) + { + dv2++; + } + + kernel.Add (dng_point (dv1, dh2), + LinearWeight1 (dv1, dv2) * w2); + + kernel.Add (dng_point (dv2, dh2), + LinearWeight2 (dv1, dv2) * w2); + + } + + } + + // Deal with temp scale case. + + if (tempScale == dng_point (2, 2)) + { + + fPatRows /= tempScale.v; + fPatCols /= tempScale.h; + + for (patRow = 0; patRow < fPatRows; patRow++) + { + + for (patCol = 0; patCol < fPatCols; patCol++) + { + + int32 patRow2 = patRow << 1; + int32 patCol2 = patCol << 1; + + dng_bilinear_kernel &kernel = fKernel [patRow2] [patCol2]; + + for (j = 0; j < kernel.fCount; j++) + { + + int32 x = patRow2 + kernel.fDelta [j].v; + + if ((x & 3) != 0) + { + x = (x & ~3) + 2; + } + + kernel.fDelta [j].v = ((x - patRow2) >> 1); + + x = patCol2 + kernel.fDelta [j].h; + + if ((x & 3) != 0) + { + x = (x & ~3) + 2; + } + + kernel.fDelta [j].h = ((x - patCol2) >> 1); + + } + + kernel.Finalize (fScale, + patRow, + patCol, + rowStep, + colStep); + + fCounts [patRow] [patCol] = kernel.fCount; + fOffsets [patRow] [patCol] = kernel.fOffset; + fWeights16 [patRow] [patCol] = kernel.fWeight16; + fWeights32 [patRow] [patCol] = kernel.fWeight32; + + } + + } + + } + + // Non-temp scale case. + + else + { + + for (patRow = 0; patRow < fPatRows; patRow++) + { + + for (patCol = 0; patCol < fPatCols; patCol++) + { + + dng_bilinear_kernel &kernel = fKernel [patRow] [patCol]; + + kernel.Finalize (fScale, + patRow, + patCol, + rowStep, + colStep); + + fCounts [patRow] [patCol] = kernel.fCount; + fOffsets [patRow] [patCol] = kernel.fOffset; + fWeights16 [patRow] [patCol] = kernel.fWeight16; + fWeights32 [patRow] [patCol] = kernel.fWeight32; + + } + + } + + } + + } + +/*****************************************************************************/ + +class dng_bilinear_interpolator + { + + private: + + dng_bilinear_pattern fPattern [kMaxColorPlanes]; + + public: + + dng_bilinear_interpolator (const dng_mosaic_info &info, + int32 rowStep, + int32 colStep); + + void Interpolate (dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer); + + }; + +/*****************************************************************************/ + +dng_bilinear_interpolator::dng_bilinear_interpolator (const dng_mosaic_info &info, + int32 rowStep, + int32 colStep) + { + + for (uint32 dstPlane = 0; dstPlane < info.fColorPlanes; dstPlane++) + { + + fPattern [dstPlane] . Calculate (info, + dstPlane, + rowStep, + colStep); + + } + + } + +/*****************************************************************************/ + +void dng_bilinear_interpolator::Interpolate (dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) + { + + uint32 patCols = fPattern [0] . fPatCols; + uint32 patRows = fPattern [0] . fPatRows; + + dng_point scale = fPattern [0] . fScale; + + uint32 sRowShift = scale.v - 1; + uint32 sColShift = scale.h - 1; + + int32 dstCol = dstBuffer.fArea.l; + + int32 srcCol = dstCol >> sColShift; + + uint32 patPhase = dstCol % patCols; + + for (int32 dstRow = dstBuffer.fArea.t; + dstRow < dstBuffer.fArea.b; + dstRow++) + { + + int32 srcRow = dstRow >> sRowShift; + + uint32 patRow = dstRow % patRows; + + for (uint32 dstPlane = 0; + dstPlane < dstBuffer.fPlanes; + dstPlane++) + { + + const void *sPtr = srcBuffer.ConstPixel (srcRow, + srcCol, + srcBuffer.fPlane); + + void *dPtr = dstBuffer.DirtyPixel (dstRow, + dstCol, + dstPlane); + + if (dstBuffer.fPixelType == ttShort) + { + + DoBilinearRow16 ((const uint16 *) sPtr, + (uint16 *) dPtr, + dstBuffer.fArea.W (), + patPhase, + patCols, + fPattern [dstPlane].fCounts [patRow], + fPattern [dstPlane].fOffsets [patRow], + fPattern [dstPlane].fWeights16 [patRow], + sColShift); + + } + + else + { + + DoBilinearRow32 ((const real32 *) sPtr, + (real32 *) dPtr, + dstBuffer.fArea.W (), + patPhase, + patCols, + fPattern [dstPlane].fCounts [patRow], + fPattern [dstPlane].fOffsets [patRow], + fPattern [dstPlane].fWeights32 [patRow], + sColShift); + + } + + } + + } + + } + +/*****************************************************************************/ + +class dng_fast_interpolator: public dng_filter_task + { + + protected: + + const dng_mosaic_info &fInfo; + + dng_point fDownScale; + + uint32 fFilterColor [kMaxCFAPattern] [kMaxCFAPattern]; + + public: + + dng_fast_interpolator (const dng_mosaic_info &info, + const dng_image &srcImage, + dng_image &dstImage, + const dng_point &downScale, + uint32 srcPlane); + + virtual dng_rect SrcArea (const dng_rect &dstArea); + + virtual void ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer); + + }; + +/*****************************************************************************/ + +dng_fast_interpolator::dng_fast_interpolator (const dng_mosaic_info &info, + const dng_image &srcImage, + dng_image &dstImage, + const dng_point &downScale, + uint32 srcPlane) + + : dng_filter_task (srcImage, + dstImage) + + , fInfo (info ) + , fDownScale (downScale) + + { + + fSrcPlane = srcPlane; + fSrcPlanes = 1; + + fSrcPixelType = ttShort; + fDstPixelType = ttShort; + + fSrcRepeat = fInfo.fCFAPatternSize; + + fUnitCell = fInfo.fCFAPatternSize; + + fMaxTileSize = dng_point (256 / fDownScale.v, + 256 / fDownScale.h); + + fMaxTileSize.h = Max_int32 (fMaxTileSize.h, fUnitCell.h); + fMaxTileSize.v = Max_int32 (fMaxTileSize.v, fUnitCell.v); + + // Find color map. + + { + + for (int32 r = 0; r < fInfo.fCFAPatternSize.v; r++) + { + + for (int32 c = 0; c < fInfo.fCFAPatternSize.h; c++) + { + + uint8 key = fInfo.fCFAPattern [r] [c]; + + for (uint32 index = 0; index < fInfo.fColorPlanes; index++) + { + + if (key == fInfo.fCFAPlaneColor [index]) + { + + fFilterColor [r] [c] = index; + + break; + + } + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +dng_rect dng_fast_interpolator::SrcArea (const dng_rect &dstArea) + { + + return dng_rect (dstArea.t * fDownScale.v, + dstArea.l * fDownScale.h, + dstArea.b * fDownScale.v, + dstArea.r * fDownScale.h); + + } + +/*****************************************************************************/ + +void dng_fast_interpolator::ProcessArea (uint32 /* threadIndex */, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) + { + + dng_rect srcArea = srcBuffer.fArea; + dng_rect dstArea = dstBuffer.fArea; + + // Downsample buffer. + + int32 srcRow = srcArea.t; + + uint32 srcRowPhase1 = 0; + uint32 srcRowPhase2 = 0; + + uint32 patRows = fInfo.fCFAPatternSize.v; + uint32 patCols = fInfo.fCFAPatternSize.h; + + uint32 cellRows = fDownScale.v; + uint32 cellCols = fDownScale.h; + + uint32 plane; + uint32 planes = fInfo.fColorPlanes; + + int32 dstPlaneStep = dstBuffer.fPlaneStep; + + uint32 total [kMaxColorPlanes]; + uint32 count [kMaxColorPlanes]; + + for (plane = 0; plane < planes; plane++) + { + total [plane] = 0; + count [plane] = 0; + } + + for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++) + { + + const uint16 *sPtr = srcBuffer.ConstPixel_uint16 (srcRow, + srcArea.l, + fSrcPlane); + + uint16 *dPtr = dstBuffer.DirtyPixel_uint16 (dstRow, + dstArea.l, + 0); + + uint32 srcColPhase1 = 0; + uint32 srcColPhase2 = 0; + + for (int32 dstCol = dstArea.l; dstCol < dstArea.r; dstCol++) + { + + const uint16 *ssPtr = sPtr; + + srcRowPhase2 = srcRowPhase1; + + for (uint32 cellRow = 0; cellRow < cellRows; cellRow++) + { + + const uint32 *filterRow = fFilterColor [srcRowPhase2]; + + if (++srcRowPhase2 == patRows) + { + srcRowPhase2 = 0; + } + + srcColPhase2 = srcColPhase1; + + for (uint32 cellCol = 0; cellCol < cellCols; cellCol++) + { + + uint32 color = filterRow [srcColPhase2]; + + if (++srcColPhase2 == patCols) + { + srcColPhase2 = 0; + } + + total [color] += (uint32) ssPtr [cellCol]; + count [color] ++; + + } + + ssPtr += srcBuffer.fRowStep; + + } + + for (plane = 0; plane < planes; plane++) + { + + uint32 t = total [plane]; + uint32 c = count [plane]; + + dPtr [plane * dstPlaneStep] = (uint16) ((t + (c >> 1)) / c); + + total [plane] = 0; + count [plane] = 0; + + } + + srcColPhase1 = srcColPhase2; + + sPtr += cellCols; + + dPtr ++; + + } + + srcRowPhase1 = srcRowPhase2; + + srcRow += cellRows; + + } + + } + +/*****************************************************************************/ + +dng_mosaic_info::dng_mosaic_info () + + : fCFAPatternSize () + , fColorPlanes (0) + , fCFALayout (1) + , fBayerGreenSplit (0) + , fSrcSize () + , fCroppedSize () + , fAspectRatio (1.0) + + { + + } + +/*****************************************************************************/ + +dng_mosaic_info::~dng_mosaic_info () + { + + } + +/*****************************************************************************/ + +void dng_mosaic_info::Parse (dng_host & /* host */, + dng_stream & /* stream */, + dng_info &info) + { + + // Find main image IFD. + + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex].Get (); + + // This information only applies to CFA images. + + if (rawIFD.fPhotometricInterpretation != piCFA) + { + return; + } + + // Copy CFA pattern. + + fCFAPatternSize.v = rawIFD.fCFARepeatPatternRows; + fCFAPatternSize.h = rawIFD.fCFARepeatPatternCols; + + for (int32 j = 0; j < fCFAPatternSize.v; j++) + { + for (int32 k = 0; k < fCFAPatternSize.h; k++) + { + fCFAPattern [j] [k] = rawIFD.fCFAPattern [j] [k]; + } + } + + // Copy CFA plane information. + + fColorPlanes = info.fShared->fCameraProfile.fColorPlanes; + + for (uint32 n = 0; n < fColorPlanes; n++) + { + fCFAPlaneColor [n] = rawIFD.fCFAPlaneColor [n]; + } + + // Copy CFA layout information. + + fCFALayout = rawIFD.fCFALayout; + + // Green split value for Bayer patterns. + + fBayerGreenSplit = rawIFD.fBayerGreenSplit; + + } + +/*****************************************************************************/ + +void dng_mosaic_info::PostParse (dng_host & /* host */, + dng_negative &negative) + { + + // Keep track of source image size. + + fSrcSize = negative.Stage2Image ()->Size (); + + // Default cropped size. + + fCroppedSize.v = Round_int32 (negative.DefaultCropSizeV ().As_real64 ()); + fCroppedSize.h = Round_int32 (negative.DefaultCropSizeH ().As_real64 ()); + + // Pixel aspect ratio. + + fAspectRatio = negative.DefaultScaleH ().As_real64 () / + negative.DefaultScaleV ().As_real64 (); + + } + +/*****************************************************************************/ + +bool dng_mosaic_info::SetFourColorBayer () + { + + if (fCFAPatternSize != dng_point (2, 2)) + { + return false; + } + + if (fColorPlanes != 3) + { + return false; + } + + uint8 color0 = fCFAPlaneColor [0]; + uint8 color1 = fCFAPlaneColor [1]; + uint8 color2 = fCFAPlaneColor [2]; + + // Look for color 1 repeated twice in a diagonal. + + if ((fCFAPattern [0] [0] == color1 && fCFAPattern [1] [1] == color1) || + (fCFAPattern [0] [1] == color1 && fCFAPattern [1] [0] == color1)) + { + + // OK, this looks like a Bayer pattern. + + // Find unused color code. + + uint8 color3 = 0; + + while (color3 == color0 || + color3 == color1 || + color3 == color2) + { + color3++; + } + + // Switch the four color mosaic. + + fColorPlanes = 4; + + fCFAPlaneColor [3] = color3; + + // Replace the "green" in the "blue" rows with the new color. + + if (fCFAPattern [0] [0] == color0) + { + fCFAPattern [1] [0] = color3; + } + + else if (fCFAPattern [0] [1] == color0) + { + fCFAPattern [1] [1] = color3; + } + + else if (fCFAPattern [1] [0] == color0) + { + fCFAPattern [0] [0] = color3; + } + + else + { + fCFAPattern [0] [1] = color3; + } + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +dng_point dng_mosaic_info::FullScale () const + { + + switch (fCFALayout) + { + + // Staggered layouts with offset columns double the row count + // during interpolation. + + case 2: + case 3: + return dng_point (2, 1); + + // Staggered layouts with offset rows double the column count + // during interpolation. + + case 4: + case 5: + return dng_point (1, 2); + + // Otherwise there is no size change during interpolation. + + default: + break; + + } + + return dng_point (1, 1); + + } + +/*****************************************************************************/ + +bool dng_mosaic_info::IsSafeDownScale (const dng_point &downScale) const + { + + if (downScale.v >= fCFAPatternSize.v && + downScale.h >= fCFAPatternSize.h) + { + + return true; + + } + + dng_point test; + + test.v = Min_int32 (downScale.v, fCFAPatternSize.v); + test.h = Min_int32 (downScale.h, fCFAPatternSize.h); + + for (int32 phaseV = 0; phaseV <= fCFAPatternSize.v - test.v; phaseV++) + { + + for (int32 phaseH = 0; phaseH <= fCFAPatternSize.h - test.h; phaseH++) + { + + uint32 plane; + + bool contains [kMaxColorPlanes]; + + for (plane = 0; plane < fColorPlanes; plane++) + { + + contains [plane] = false; + + } + + for (int32 srcRow = 0; srcRow < test.v; srcRow++) + { + + for (int32 srcCol = 0; srcCol < test.h; srcCol++) + { + + uint8 srcKey = fCFAPattern [srcRow + phaseV] + [srcCol + phaseH]; + + for (plane = 0; plane < fColorPlanes; plane++) + { + + if (srcKey == fCFAPlaneColor [plane]) + { + + contains [plane] = true; + + } + + } + + + } + + } + + for (plane = 0; plane < fColorPlanes; plane++) + { + + if (!contains [plane]) + { + + return false; + + } + + } + + } + + } + + return true; + + } + +/*****************************************************************************/ + +uint32 dng_mosaic_info::SizeForDownScale (const dng_point &downScale) const + { + + uint32 sizeV = Max_uint32 (1, (fCroppedSize.v + (downScale.v >> 1)) / downScale.v); + uint32 sizeH = Max_uint32 (1, (fCroppedSize.h + (downScale.h >> 1)) / downScale.h); + + return Max_int32 (sizeV, sizeH); + + } + +/*****************************************************************************/ + +bool dng_mosaic_info::ValidSizeDownScale (const dng_point &downScale, + uint32 minSize) const + { + + const int32 kMaxDownScale = 64; + + if (downScale.h > kMaxDownScale || + downScale.v > kMaxDownScale) + { + + return false; + + } + + return SizeForDownScale (downScale) >= minSize; + + } + +/*****************************************************************************/ + +dng_point dng_mosaic_info::DownScale (uint32 minSize, + uint32 prefSize, + real64 cropFactor) const + { + + dng_point bestScale (1, 1); + + if (prefSize && IsColorFilterArray ()) + { + + // Adjust sizes for crop factor. + + minSize = Round_uint32 (minSize / cropFactor); + prefSize = Round_uint32 (prefSize / cropFactor); + + prefSize = Max_uint32 (prefSize, minSize); + + // Start by assuming we need the full size image. + + int32 bestSize = SizeForDownScale (bestScale); + + // Find size of nearly square cell. + + dng_point squareCell (1, 1); + + if (fAspectRatio < 1.0 / 1.8) + { + + squareCell.h = Min_int32 (4, Round_int32 (1.0 / fAspectRatio)); + + } + + if (fAspectRatio > 1.8) + { + + squareCell.v = Min_int32 (4, Round_int32 (fAspectRatio)); + + } + + // Find minimum safe cell size. + + dng_point testScale = squareCell; + + while (!IsSafeDownScale (testScale)) + { + + testScale.v += squareCell.v; + testScale.h += squareCell.h; + + } + + // See if this scale is usable. + + if (!ValidSizeDownScale (testScale, minSize)) + { + + // We cannot downsample at all... + + return bestScale; + + } + + // See if this is closer to the preferred size. + + int32 testSize = SizeForDownScale (testScale); + + if (Abs_int32 (testSize - (int32) prefSize) <= + Abs_int32 (bestSize - (int32) prefSize)) + { + bestScale = testScale; + bestSize = testSize; + } + + else + { + return bestScale; + } + + // Now keep adding square cells as long as possible. + + while (true) + { + + testScale.v += squareCell.v; + testScale.h += squareCell.h; + + if (IsSafeDownScale (testScale)) + { + + if (!ValidSizeDownScale (testScale, minSize)) + { + return bestScale; + } + + // See if this is closer to the preferred size. + + testSize = SizeForDownScale (testScale); + + if (Abs_int32 (testSize - (int32) prefSize) <= + Abs_int32 (bestSize - (int32) prefSize)) + { + bestScale = testScale; + bestSize = testSize; + } + + else + { + return bestScale; + } + + } + + } + + } + + return bestScale; + + } + +/*****************************************************************************/ + +dng_point dng_mosaic_info::DstSize (const dng_point &downScale) const + { + + if (downScale == dng_point (1, 1)) + { + + dng_point scale = FullScale (); + + return dng_point (fSrcSize.v * scale.v, + fSrcSize.h * scale.h); + + } + + const int32 kMaxDownScale = 64; + + if (downScale.h > kMaxDownScale || + downScale.v > kMaxDownScale) + { + + return dng_point (0, 0); + + } + + dng_point size; + + size.v = Max_int32 (1, (fSrcSize.v + (downScale.v >> 1)) / downScale.v); + size.h = Max_int32 (1, (fSrcSize.h + (downScale.h >> 1)) / downScale.h); + + return size; + + } + +/*****************************************************************************/ + +void dng_mosaic_info::InterpolateGeneric (dng_host &host, + dng_negative & /* negative */, + const dng_image &srcImage, + dng_image &dstImage, + uint32 srcPlane) const + { + + // Find destination to source bit shifts. + + dng_point scale = FullScale (); + + uint32 srcShiftV = scale.v - 1; + uint32 srcShiftH = scale.h - 1; + + // Find tile sizes. + + const uint32 kMaxDstTileRows = 128; + const uint32 kMaxDstTileCols = 128; + + dng_point dstTileSize = dstImage.RepeatingTile ().Size (); + + dstTileSize.v = Min_int32 (dstTileSize.v, kMaxDstTileRows); + dstTileSize.h = Min_int32 (dstTileSize.h, kMaxDstTileCols); + + dng_point srcTileSize = dstTileSize; + + srcTileSize.v >>= srcShiftV; + srcTileSize.h >>= srcShiftH; + + srcTileSize.v += fCFAPatternSize.v * 2; + srcTileSize.h += fCFAPatternSize.h * 2; + + // Allocate source buffer. + + dng_pixel_buffer srcBuffer; + + srcBuffer.fPlane = srcPlane; + + srcBuffer.fRowStep = srcTileSize.h; + + srcBuffer.fPixelType = srcImage.PixelType (); + srcBuffer.fPixelSize = srcImage.PixelSize (); + + uint32 srcBufferSize = srcBuffer.fPixelSize * + srcBuffer.fRowStep * + srcTileSize.v; + + AutoPtr srcData (host.Allocate (srcBufferSize)); + + srcBuffer.fData = srcData->Buffer (); + + // Allocate destination buffer. + + dng_pixel_buffer dstBuffer; + + dstBuffer.fPlanes = fColorPlanes; + + dstBuffer.fRowStep = dstTileSize.h * fColorPlanes; + dstBuffer.fPlaneStep = dstTileSize.h; + + dstBuffer.fPixelType = dstImage.PixelType (); + dstBuffer.fPixelSize = dstImage.PixelSize (); + + uint32 dstBufferSize = dstBuffer.fPixelSize * + dstBuffer.fRowStep * + dstTileSize.v; + + AutoPtr dstData (host.Allocate (dstBufferSize)); + + dstBuffer.fData = dstData->Buffer (); + + // Create interpolator. + + AutoPtr interpolator (new dng_bilinear_interpolator (*this, + srcBuffer.fRowStep, + srcBuffer.fColStep)); + + // Iterate over destination tiles. + + dng_rect dstArea; + + dng_tile_iterator iter1 (dstImage, dstImage.Bounds ()); + + while (iter1.GetOneTile (dstArea)) + { + + // Break into buffer sized tiles. + + dng_rect dstTile; + + dng_tile_iterator iter2 (dstTileSize, dstArea); + + while (iter2.GetOneTile (dstTile)) + { + + host.SniffForAbort (); + + // Setup buffers for this tile. + + dng_rect srcTile (dstTile); + + srcTile.t >>= srcShiftV; + srcTile.b >>= srcShiftV; + + srcTile.l >>= srcShiftH; + srcTile.r >>= srcShiftH; + + srcTile.t -= fCFAPatternSize.v; + srcTile.b += fCFAPatternSize.v; + + srcTile.l -= fCFAPatternSize.h; + srcTile.r += fCFAPatternSize.h; + + srcBuffer.fArea = srcTile; + dstBuffer.fArea = dstTile; + + // Get source data. + + srcImage.Get (srcBuffer, + dng_image::edge_repeat, + fCFAPatternSize.v, + fCFAPatternSize.h); + + // Process data. + + interpolator->Interpolate (srcBuffer, + dstBuffer); + + // Save results. + + dstImage.Put (dstBuffer); + + } + + } + + } + +/*****************************************************************************/ + +void dng_mosaic_info::InterpolateFast (dng_host &host, + dng_negative & /* negative */, + const dng_image &srcImage, + dng_image &dstImage, + const dng_point &downScale, + uint32 srcPlane) const + { + + // Create fast interpolator task. + + dng_fast_interpolator interpolator (*this, + srcImage, + dstImage, + downScale, + srcPlane); + + // Find area to process. + + dng_rect bounds = dstImage.Bounds (); + + // Do the interpolation. + + host.PerformAreaTask (interpolator, + bounds); + + } + +/*****************************************************************************/ + +void dng_mosaic_info::Interpolate (dng_host &host, + dng_negative &negative, + const dng_image &srcImage, + dng_image &dstImage, + const dng_point &downScale, + uint32 srcPlane) const + { + + if (downScale == dng_point (1, 1)) + { + + InterpolateGeneric (host, + negative, + srcImage, + dstImage, + srcPlane); + + } + + else + { + + InterpolateFast (host, + negative, + srcImage, + dstImage, + downScale, + srcPlane); + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_mosaic_info.h b/source/lib/dng_sdk/dng_mosaic_info.h new file mode 100644 index 0000000..477c105 --- /dev/null +++ b/source/lib/dng_sdk/dng_mosaic_info.h @@ -0,0 +1,200 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_mosaic_info.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Support for descriptive information about color filter array patterns. + */ + +/*****************************************************************************/ + +#ifndef __dng_mosaic_info__ +#define __dng_mosaic_info__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_rect.h" +#include "dng_sdk_limits.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief Support for describing color filter array patterns and manipulating mosaic sample data. +/// +/// See CFAPattern tag in \ref spec_tiff_ep "TIFF/EP specification" and CFAPlaneColor, CFALayout, and BayerGreenSplit +/// tags in the \ref spec_dng "DNG 1.1.0 specification". + +class dng_mosaic_info + { + + public: + + /// Size of fCFAPattern. + + dng_point fCFAPatternSize; + + /// CFA pattern from CFAPattern tag in the \ref spec_tiff_ep "TIFF/EP specification." + + uint8 fCFAPattern [kMaxCFAPattern] [kMaxCFAPattern]; + + /// Number of color planes in DNG input. + + uint32 fColorPlanes; + + uint8 fCFAPlaneColor [kMaxColorPlanes]; + + /// Value of CFALayout tag in the \ref spec_dng "DNG 1.3 specification." + /// CFALayout describes the spatial layout of the CFA. The currently defined values are: + /// - 1 = Rectangular (or square) layout. + /// - 2 = Staggered layout A: even columns are offset down by 1/2 row. + /// - 3 = Staggered layout B: even columns are offset up by 1/2 row. + /// - 4 = Staggered layout C: even rows are offset right by 1/2 column. + /// - 5 = Staggered layout D: even rows are offset left by 1/2 column. + /// - 6 = Staggered layout E: even rows are offset up by 1/2 row, even columns are offset left by 1/2 column. + /// - 7 = Staggered layout F: even rows are offset up by 1/2 row, even columns are offset right by 1/2 column. + /// - 8 = Staggered layout G: even rows are offset down by 1/2 row, even columns are offset left by 1/2 column. + /// - 9 = Staggered layout H: even rows are offset down by 1/2 row, even columns are offset right by 1/2 column. + + uint32 fCFALayout; + + /// Value of BayerGreeSplit tag in DNG file. + /// BayerGreenSplit only applies to CFA images using a Bayer pattern filter array. This tag + /// specifies, in arbitrary units, how closely the values of the green pixels in the blue/green rows + /// track the values of the green pixels in the red/green rows. + /// + /// A value of zero means the two kinds of green pixels track closely, while a non-zero value + /// means they sometimes diverge. The useful range for this tag is from 0 (no divergence) to about + /// 5000 (large divergence). + + uint32 fBayerGreenSplit; + + protected: + + dng_point fSrcSize; + + dng_point fCroppedSize; + + real64 fAspectRatio; + + public: + + dng_mosaic_info (); + + virtual ~dng_mosaic_info (); + + virtual void Parse (dng_host &host, + dng_stream &stream, + dng_info &info); + + virtual void PostParse (dng_host &host, + dng_negative &negative); + + /// Returns whether the RAW data in this DNG file from a color filter array (mosaiced) source. + /// \retval true if this DNG file is from a color filter array (mosiaced) source. + + bool IsColorFilterArray () const + { + return fCFAPatternSize != dng_point (0, 0); + } + + /// Enable generating four-plane output from three-plane Bayer input. + /// Extra plane is a second version of the green channel. First green is produced + /// using green mosaic samples from one set of rows/columns (even/odd) and the second + /// green channel is produced using the other set of rows/columns. One can compare the + /// two versions to judge whether BayerGreenSplit needs to be set for a given input source. + + virtual bool SetFourColorBayer (); + + /// Returns scaling factor relative to input size needed to capture output data. + /// Staggered (or rotated) sensing arrays are produced to a larger output than the number of input samples. + /// This method indicates how much larger. + /// \retval a point with integer scaling factors for the horizotal and vertical dimensions. + + virtual dng_point FullScale () const; + + /// Returns integer factors by which mosaic data must be downsampled to produce an image which is as close + /// to prefSize as possible in longer dimension, but no smaller than minSize. + /// \param minSize Number of pixels as minium for longer dimension of downsampled image. + /// \param prefSize Number of pixels as target for longer dimension of downsampled image. + /// \param cropFactor Faction of the image to be used after cropping. + /// \retval Point containing integer factors by which image must be downsampled. + + virtual dng_point DownScale (uint32 minSize, + uint32 prefSize, + real64 cropFactor) const; + + /// Return size of demosaiced image for passed in downscaling factor. + /// \param downScale Integer downsampling factor obtained from DownScale method. + /// \retval Size of resulting demosaiced image. + + virtual dng_point DstSize (const dng_point &downScale) const; + + /// Demosaic interpolation of a single plane for non-downsampled case. + /// \param host dng_host to use for buffer allocation requests, user cancellation testing, and progress updates. + /// \param negative DNG negative of mosaiced data. + /// \param srcImage Source image for mosaiced data. + /// \param dstImage Destination image for resulting interpolated data. + /// \param srcPlane Which plane to interpolate. + + virtual void InterpolateGeneric (dng_host &host, + dng_negative &negative, + const dng_image &srcImage, + dng_image &dstImage, + uint32 srcPlane = 0) const; + + /// Demosaic interpolation of a single plane for downsampled case. + /// \param host dng_host to use for buffer allocation requests, user cancellation testing, and progress updates. + /// \param negative DNG negative of mosaiced data. + /// \param srcImage Source image for mosaiced data. + /// \param dstImage Destination image for resulting interpolated data. + /// \param downScale Amount (in horizontal and vertical) by which to subsample image. + /// \param srcPlane Which plane to interpolate. + + virtual void InterpolateFast (dng_host &host, + dng_negative &negative, + const dng_image &srcImage, + dng_image &dstImage, + const dng_point &downScale, + uint32 srcPlane = 0) const; + + /// Demosaic interpolation of a single plane. Chooses between generic and fast interpolators based on parameters. + /// \param host dng_host to use for buffer allocation requests, user cancellation testing, and progress updates. + /// \param negative DNG negative of mosaiced data. + /// \param srcImage Source image for mosaiced data. + /// \param dstImage Destination image for resulting interpolated data. + /// \param downScale Amount (in horizontal and vertical) by which to subsample image. + /// \param srcPlane Which plane to interpolate. + + virtual void Interpolate (dng_host &host, + dng_negative &negative, + const dng_image &srcImage, + dng_image &dstImage, + const dng_point &downScale, + uint32 srcPlane = 0) const; + + protected: + + virtual bool IsSafeDownScale (const dng_point &downScale) const; + + uint32 SizeForDownScale (const dng_point &downScale) const; + + virtual bool ValidSizeDownScale (const dng_point &downScale, + uint32 minSize) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_mutex.cpp b/source/lib/dng_sdk/dng_mutex.cpp new file mode 100644 index 0000000..7451c7c --- /dev/null +++ b/source/lib/dng_sdk/dng_mutex.cpp @@ -0,0 +1,394 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_mutex.cpp#3 $ */ +/* $DateTime: 2012/09/05 12:31:51 $ */ +/* $Change: 847652 $ */ +/* $Author: tknoll $ */ + +#include "dng_mutex.h" + +#include "dng_assertions.h" +#include "dng_exceptions.h" + +#include + +/*****************************************************************************/ + +#if qDNGThreadSafe + +namespace + { + + class InnermostMutexHolder + { + + private: + + pthread_key_t fInnermostMutexKey; + + public: + + InnermostMutexHolder () + + : fInnermostMutexKey () + + { + + int result = pthread_key_create (&fInnermostMutexKey, NULL); + + DNG_ASSERT (result == 0, "pthread_key_create failed."); + + if (result != 0) + ThrowProgramError (); + + } + + ~InnermostMutexHolder () + { + + pthread_key_delete (fInnermostMutexKey); + + } + + void SetInnermostMutex (dng_mutex *mutex) + { + + int result; + + result = pthread_setspecific (fInnermostMutexKey, (void *)mutex); + + DNG_ASSERT (result == 0, "pthread_setspecific failed."); + + #if 0 // Hard failure here was causing crash on quit. + + if (result != 0) + ThrowProgramError (); + + #endif + + } + + dng_mutex *GetInnermostMutex () + { + + void *result = pthread_getspecific (fInnermostMutexKey); + + return reinterpret_cast (result); + + } + + }; + + InnermostMutexHolder gInnermostMutexHolder; + + } + +#endif + +/*****************************************************************************/ + +dng_mutex::dng_mutex (const char *mutexName, uint32 mutexLevel) + + #if qDNGThreadSafe + + : fPthreadMutex () + , fMutexLevel (mutexLevel) + , fRecursiveLockCount (0) + , fPrevHeldMutex (NULL) + , fMutexName (mutexName) + + #endif + + { + + #if qDNGThreadSafe + + if (pthread_mutex_init (&fPthreadMutex, NULL) != 0) + { + ThrowMemoryFull (); + } + + #endif + + } + +/*****************************************************************************/ + +dng_mutex::~dng_mutex () + { + + #if qDNGThreadSafe + + pthread_mutex_destroy (&fPthreadMutex); + + #endif + + } + +/*****************************************************************************/ + +void dng_mutex::Lock () + { + + #if qDNGThreadSafe + + dng_mutex *innermostMutex = gInnermostMutexHolder.GetInnermostMutex (); + + if (innermostMutex != NULL) + { + + if (innermostMutex == this) + { + + fRecursiveLockCount++; + + return; + + } + + bool lockOrderPreserved = fMutexLevel > innermostMutex->fMutexLevel /* || + (fMutexLevel == innermostMutex->fMutexLevel && innermostMutex < this) */; + + if (!lockOrderPreserved) + { + + DNG_REPORT ("Lock ordering violation."); + + #if qDNGDebug + + dng_show_message_f ("This mutex: %s v Innermost mutex: %s", + this->MutexName (), + innermostMutex->MutexName ()); + + #endif + + } + + } + + pthread_mutex_lock (&fPthreadMutex); + + fPrevHeldMutex = innermostMutex; + + gInnermostMutexHolder.SetInnermostMutex (this); + + #endif + + } + +/*****************************************************************************/ + +void dng_mutex::Unlock () + { + + #if qDNGThreadSafe + + DNG_ASSERT (gInnermostMutexHolder.GetInnermostMutex () == this, "Mutexes unlocked out of order!!!"); + + if (fRecursiveLockCount > 0) + { + + fRecursiveLockCount--; + + return; + + } + + gInnermostMutexHolder.SetInnermostMutex (fPrevHeldMutex); + + fPrevHeldMutex = NULL; + + pthread_mutex_unlock (&fPthreadMutex); + + #endif + + } + +/*****************************************************************************/ + +const char *dng_mutex::MutexName () const + { + + #if qDNGThreadSafe + + if (fMutexName) + return fMutexName; + + #endif + + return "< unknown >"; + + } + +/*****************************************************************************/ + +dng_lock_mutex::dng_lock_mutex (dng_mutex *mutex) + + : fMutex (mutex) + + { + + if (fMutex) + fMutex->Lock (); + + } + +/*****************************************************************************/ + +dng_lock_mutex::~dng_lock_mutex () + { + + if (fMutex) + fMutex->Unlock (); + + } + +/*****************************************************************************/ + +dng_unlock_mutex::dng_unlock_mutex (dng_mutex *mutex) + + : fMutex (mutex) + + { + + if (fMutex) + fMutex->Unlock (); + + } + +/*****************************************************************************/ + +dng_unlock_mutex::~dng_unlock_mutex () + { + + if (fMutex) + fMutex->Lock (); + + } + +/*****************************************************************************/ + +#if qDNGThreadSafe + +/*****************************************************************************/ + +dng_condition::dng_condition () + + : fPthreadCondition () + + { + + int result; + + result = pthread_cond_init (&fPthreadCondition, NULL); + + DNG_ASSERT (result == 0, "pthread_cond_init failed."); + + if (result != 0) + { + ThrowProgramError (); + } + + } + +/*****************************************************************************/ + +dng_condition::~dng_condition () + { + + pthread_cond_destroy (&fPthreadCondition); + + } + +/*****************************************************************************/ + +bool dng_condition::Wait (dng_mutex &mutex, double timeoutSecs) + { + + bool timedOut = false; + + dng_mutex *innermostMutex = gInnermostMutexHolder.GetInnermostMutex (); + + DNG_ASSERT (innermostMutex == &mutex, "Attempt to wait on non-innermost mutex."); + + innermostMutex = mutex.fPrevHeldMutex; + + gInnermostMutexHolder.SetInnermostMutex (innermostMutex); + + mutex.fPrevHeldMutex = NULL; + + if (timeoutSecs < 0) + { + + pthread_cond_wait (&fPthreadCondition, &mutex.fPthreadMutex); + + } + + else + { + + struct timespec now; + + dng_pthread_now (&now); + + timeoutSecs += now.tv_sec; + timeoutSecs += now.tv_nsec / 1000000000.0; + + now.tv_sec = (long) timeoutSecs; + now.tv_nsec = (long) ((timeoutSecs - now.tv_sec) * 1000000000); + + timedOut = (pthread_cond_timedwait (&fPthreadCondition, &mutex.fPthreadMutex, &now) == ETIMEDOUT); + + } + + mutex.fPrevHeldMutex = innermostMutex; + + gInnermostMutexHolder.SetInnermostMutex (&mutex); + + return !timedOut; + + } + +/*****************************************************************************/ + +void dng_condition::Signal () + { + + int result; + + result = pthread_cond_signal (&fPthreadCondition); + + DNG_ASSERT (result == 0, "pthread_cond_signal failed."); + + if (result != 0) + ThrowProgramError (); + + } + +/*****************************************************************************/ + +void dng_condition::Broadcast () + { + + int result; + + result = pthread_cond_broadcast (&fPthreadCondition); + + DNG_ASSERT (result == 0, "pthread_cond_broadcast failed."); + + if (result != 0) + ThrowProgramError (); + + } + +/*****************************************************************************/ + +#endif // qDNGThreadSafe + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_mutex.h b/source/lib/dng_sdk/dng_mutex.h new file mode 100644 index 0000000..b067728 --- /dev/null +++ b/source/lib/dng_sdk/dng_mutex.h @@ -0,0 +1,177 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_mutex.h#2 $ */ +/* $DateTime: 2012/09/05 12:31:51 $ */ +/* $Change: 847652 $ */ +/* $Author: tknoll $ */ + +/******************************************************************************/ + +#ifndef __dng_mutex__ +#define __dng_mutex__ + +/******************************************************************************/ + +#include "dng_flags.h" + +/******************************************************************************/ + +#include "dng_types.h" + +#if qDNGThreadSafe + +#include "dng_pthread.h" + +#endif + +/******************************************************************************/ + +class dng_mutex + { + + public: + + enum + { + kDNGMutexLevelLeaf = 0x70000000u + }; + + dng_mutex (const char *mutexName, + uint32 mutexLevel = kDNGMutexLevelLeaf); + + virtual ~dng_mutex (); + + void Lock (); + + void Unlock (); + + const char *MutexName () const; + + protected: + + #if qDNGThreadSafe + + pthread_mutex_t fPthreadMutex; + + const uint32 fMutexLevel; + + uint32 fRecursiveLockCount; + + dng_mutex *fPrevHeldMutex; + + const char * const fMutexName; + + friend class dng_condition; + + #endif + + private: + + // Hidden copy constructor and assignment operator. + + dng_mutex (const dng_mutex &mutex); + + dng_mutex & operator= (const dng_mutex &mutex); + + }; + +/*****************************************************************************/ + +class dng_lock_mutex + { + + private: + + dng_mutex *fMutex; + + public: + + dng_lock_mutex (dng_mutex *mutex); + + ~dng_lock_mutex (); + + private: + + // Hidden copy constructor and assignment operator. + + dng_lock_mutex (const dng_lock_mutex &lock); + + dng_lock_mutex & operator= (const dng_lock_mutex &lock); + + }; + +/*****************************************************************************/ + +class dng_unlock_mutex + { + + private: + + dng_mutex *fMutex; + + public: + + dng_unlock_mutex (dng_mutex *mutex); + + ~dng_unlock_mutex (); + + private: + + // Hidden copy constructor and assignment operator. + + dng_unlock_mutex (const dng_unlock_mutex &unlock); + + dng_unlock_mutex & operator= (const dng_unlock_mutex &unlock); + + }; + +/*****************************************************************************/ + +#if qDNGThreadSafe + +/*****************************************************************************/ + +class dng_condition + { + + public: + + dng_condition (); + + ~dng_condition (); + + bool Wait (dng_mutex &mutex, double timeoutSecs = -1.0); + + void Signal (); + + void Broadcast (); + + protected: + + pthread_cond_t fPthreadCondition; + + private: + + // Hidden copy constructor and assignment operator. + + dng_condition (const dng_condition &condition); + + dng_condition & operator= (const dng_condition &condition); + + }; + +/*****************************************************************************/ + +#endif // qDNGThreadSafe + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_negative.cpp b/source/lib/dng_sdk/dng_negative.cpp new file mode 100644 index 0000000..9c3fa7d --- /dev/null +++ b/source/lib/dng_sdk/dng_negative.cpp @@ -0,0 +1,5109 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_negative.cpp#3 $ */ +/* $DateTime: 2012/06/14 20:24:41 $ */ +/* $Change: 835078 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_negative.h" + +#include "dng_1d_table.h" +#include "dng_abort_sniffer.h" +#include "dng_area_task.h" +#include "dng_assertions.h" +#include "dng_bottlenecks.h" +#include "dng_camera_profile.h" +#include "dng_color_space.h" +#include "dng_color_spec.h" +#include "dng_exceptions.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_image_writer.h" +#include "dng_info.h" +#include "dng_jpeg_image.h" +#include "dng_linearization_info.h" +#include "dng_memory_stream.h" +#include "dng_misc_opcodes.h" +#include "dng_mosaic_info.h" +#include "dng_preview.h" +#include "dng_resample.h" +#include "dng_simple_image.h" +#include "dng_tag_codes.h" +#include "dng_tag_values.h" +#include "dng_tile_iterator.h" +#include "dng_utils.h" +#include "dng_xmp.h" + +#if GPR_READING +#include "dng_read_image.h" +#endif + +/*****************************************************************************/ + +dng_noise_profile::dng_noise_profile () + + : fNoiseFunctions () + + { + + } + +/*****************************************************************************/ + +dng_noise_profile::dng_noise_profile (const std::vector &functions) + + : fNoiseFunctions (functions) + + { + + } + +/*****************************************************************************/ + +bool dng_noise_profile::IsValid () const + { + + if (NumFunctions () == 0 || NumFunctions () > kMaxColorPlanes) + { + return false; + } + + for (uint32 plane = 0; plane < NumFunctions (); plane++) + { + + if (!NoiseFunction (plane).IsValid ()) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_noise_profile::IsValidForNegative (const dng_negative &negative) const + { + + if (!(NumFunctions () == 1 || NumFunctions () == negative.ColorChannels ())) + { + return false; + } + + return IsValid (); + + } + +/*****************************************************************************/ + +const dng_noise_function & dng_noise_profile::NoiseFunction (uint32 plane) const + { + + if (NumFunctions () == 1) + { + return fNoiseFunctions.front (); + } + + DNG_REQUIRE (plane < NumFunctions (), + "Bad plane index argument for NoiseFunction ()."); + + return fNoiseFunctions [plane]; + + } + +/*****************************************************************************/ + +uint32 dng_noise_profile::NumFunctions () const + { + return (uint32) fNoiseFunctions.size (); + } + +/*****************************************************************************/ + +dng_metadata::dng_metadata (dng_host &host) + + : fHasBaseOrientation (false) + , fBaseOrientation () + , fIsMakerNoteSafe (false) + , fMakerNote () + , fExif (host.Make_dng_exif ()) + , fOriginalExif () + , fIPTCBlock () + , fIPTCOffset (kDNGStreamInvalidOffset) + , fXMP (host.Make_dng_xmp ()) + , fEmbeddedXMPDigest () + , fXMPinSidecar (false) + , fXMPisNewer (false) + , fSourceMIMI () + + { + } + +/*****************************************************************************/ + +dng_metadata::~dng_metadata () + { + } + +/******************************************************************************/ + +template< class T > +T * CloneAutoPtr (const AutoPtr< T > &ptr) + { + + return ptr.Get () ? ptr->Clone () : NULL; + + } + +/******************************************************************************/ + +template< class T, typename U > +T * CloneAutoPtr (const AutoPtr< T > &ptr, U &u) + { + + return ptr.Get () ? ptr->Clone (u) : NULL; + + } + +/******************************************************************************/ + +dng_metadata::dng_metadata (const dng_metadata &rhs, + dng_memory_allocator &allocator) + + : fHasBaseOrientation (rhs.fHasBaseOrientation) + , fBaseOrientation (rhs.fBaseOrientation) + , fIsMakerNoteSafe (rhs.fIsMakerNoteSafe) + , fMakerNote (CloneAutoPtr (rhs.fMakerNote, allocator)) + , fExif (CloneAutoPtr (rhs.fExif)) + , fOriginalExif (CloneAutoPtr (rhs.fOriginalExif)) + , fIPTCBlock (CloneAutoPtr (rhs.fIPTCBlock, allocator)) + , fIPTCOffset (rhs.fIPTCOffset) + , fXMP (CloneAutoPtr (rhs.fXMP)) + , fEmbeddedXMPDigest (rhs.fEmbeddedXMPDigest) + , fXMPinSidecar (rhs.fXMPinSidecar) + , fXMPisNewer (rhs.fXMPisNewer) + , fSourceMIMI (rhs.fSourceMIMI) + + { + + } + +/******************************************************************************/ + +dng_metadata * dng_metadata::Clone (dng_memory_allocator &allocator) const + { + + return new dng_metadata (*this, allocator); + + } + +/******************************************************************************/ + +void dng_metadata::SetBaseOrientation (const dng_orientation &orientation) + { + + fHasBaseOrientation = true; + + fBaseOrientation = orientation; + + } + +/******************************************************************************/ + +void dng_metadata::ApplyOrientation (const dng_orientation &orientation) + { + + fBaseOrientation += orientation; + + fXMP->SetOrientation (fBaseOrientation); + + } + +/*****************************************************************************/ + +void dng_metadata::ResetExif (dng_exif * newExif) + { + + fExif.Reset (newExif); + + } + +/******************************************************************************/ + +dng_memory_block * dng_metadata::BuildExifBlock (dng_memory_allocator &allocator, + const dng_resolution *resolution, + bool includeIPTC, + const dng_jpeg_preview *thumbnail) const + { + + dng_memory_stream stream (allocator); + + { + + // Create the main IFD + + dng_tiff_directory mainIFD; + + // Optionally include the resolution tags. + + dng_resolution res; + + if (resolution) + { + res = *resolution; + } + + tag_urational tagXResolution (tcXResolution, res.fXResolution); + tag_urational tagYResolution (tcYResolution, res.fYResolution); + + tag_uint16 tagResolutionUnit (tcResolutionUnit, res.fResolutionUnit); + + if (resolution) + { + mainIFD.Add (&tagXResolution ); + mainIFD.Add (&tagYResolution ); + mainIFD.Add (&tagResolutionUnit); + } + + // Optionally include IPTC block. + + tag_iptc tagIPTC (IPTCData (), + IPTCLength ()); + + if (includeIPTC && tagIPTC.Count ()) + { + mainIFD.Add (&tagIPTC); + } + + // Exif tags. + + exif_tag_set exifSet (mainIFD, + *GetExif (), + IsMakerNoteSafe (), + MakerNoteData (), + MakerNoteLength (), + false); + + // Figure out the Exif IFD offset. + + uint32 exifOffset = 8 + mainIFD.Size (); + + exifSet.Locate (exifOffset); + + // Thumbnail IFD (if any). + + dng_tiff_directory thumbIFD; + + tag_uint16 thumbCompression (tcCompression, ccOldJPEG); + + tag_urational thumbXResolution (tcXResolution, dng_urational (72, 1)); + tag_urational thumbYResolution (tcYResolution, dng_urational (72, 1)); + + tag_uint16 thumbResolutionUnit (tcResolutionUnit, ruInch); + + tag_uint32 thumbDataOffset (tcJPEGInterchangeFormat , 0); + tag_uint32 thumbDataLength (tcJPEGInterchangeFormatLength, 0); + + if (thumbnail) + { + + thumbIFD.Add (&thumbCompression); + + thumbIFD.Add (&thumbXResolution); + thumbIFD.Add (&thumbYResolution); + thumbIFD.Add (&thumbResolutionUnit); + + thumbIFD.Add (&thumbDataOffset); + thumbIFD.Add (&thumbDataLength); + + thumbDataLength.Set (thumbnail->fCompressedData->LogicalSize ()); + + uint32 thumbOffset = exifOffset + exifSet.Size (); + + mainIFD.SetChained (thumbOffset); + + thumbDataOffset.Set (thumbOffset + thumbIFD.Size ()); + + } + + // Don't write anything unless the main IFD has some tags. + + if (mainIFD.Size () != 0) + { + + // Write TIFF Header. + + stream.SetWritePosition (0); + + stream.Put_uint16 (stream.BigEndian () ? byteOrderMM : byteOrderII); + + stream.Put_uint16 (42); + + stream.Put_uint32 (8); + + // Write the IFDs. + + mainIFD.Put (stream); + + exifSet.Put (stream); + + if (thumbnail) + { + + thumbIFD.Put (stream); + + stream.Put (thumbnail->fCompressedData->Buffer (), + thumbnail->fCompressedData->LogicalSize ()); + + } + + // Trim the file to this length. + + stream.Flush (); + + stream.SetLength (stream.Position ()); + + } + + } + + return stream.AsMemoryBlock (allocator); + + } + +/******************************************************************************/ + +void dng_metadata::SetIPTC (AutoPtr &block, uint64 offset) + { + + fIPTCBlock.Reset (block.Release ()); + + fIPTCOffset = offset; + + } + +/******************************************************************************/ + +void dng_metadata::SetIPTC (AutoPtr &block) + { + + SetIPTC (block, kDNGStreamInvalidOffset); + + } + +/******************************************************************************/ + +void dng_metadata::ClearIPTC () + { + + fIPTCBlock.Reset (); + + fIPTCOffset = kDNGStreamInvalidOffset; + + } + +/*****************************************************************************/ + +const void * dng_metadata::IPTCData () const + { + + if (fIPTCBlock.Get ()) + { + + return fIPTCBlock->Buffer (); + + } + + return NULL; + + } + +/*****************************************************************************/ + +uint32 dng_metadata::IPTCLength () const + { + + if (fIPTCBlock.Get ()) + { + + return fIPTCBlock->LogicalSize (); + + } + + return 0; + + } + +/*****************************************************************************/ + +uint64 dng_metadata::IPTCOffset () const + { + + if (fIPTCBlock.Get ()) + { + + return fIPTCOffset; + + } + + return kDNGStreamInvalidOffset; + + } + +/*****************************************************************************/ + +dng_fingerprint dng_metadata::IPTCDigest (bool includePadding) const + { + + if (IPTCLength ()) + { + + dng_md5_printer printer; + + const uint8 *data = (const uint8 *) IPTCData (); + + uint32 count = IPTCLength (); + + // Because of some stupid ways of storing the IPTC data, the IPTC + // data might be padded with up to three zeros. The official Adobe + // logic is to include these zeros in the digest. However, older + // versions of the Camera Raw code did not include the padding zeros + // in the digest, so we support both methods and allow either to + // match. + + if (!includePadding) + { + + uint32 removed = 0; + + while ((removed < 3) && (count > 0) && (data [count - 1] == 0)) + { + removed++; + count--; + } + + } + + printer.Process (data, count); + + return printer.Result (); + + } + + return dng_fingerprint (); + + } + +/******************************************************************************/ + +void dng_metadata::RebuildIPTC (dng_memory_allocator &allocator, + bool padForTIFF) + { + + ClearIPTC (); + + fXMP->RebuildIPTC (*this, allocator, padForTIFF); + + dng_fingerprint digest = IPTCDigest (); + + fXMP->SetIPTCDigest (digest); + + } + +/*****************************************************************************/ + +void dng_metadata::ResetXMP (dng_xmp * newXMP) + { + + fXMP.Reset (newXMP); + + } + +/*****************************************************************************/ + +void dng_metadata::ResetXMPSidecarNewer (dng_xmp * newXMP, + bool inSidecar, + bool isNewer ) + { + + fXMP.Reset (newXMP); + + fXMPinSidecar = inSidecar; + + fXMPisNewer = isNewer; + + } + +/*****************************************************************************/ + +bool dng_metadata::SetXMP (dng_host &host, + const void *buffer, + uint32 count, + bool xmpInSidecar, + bool xmpIsNewer) + { + + bool result = false; + + try + { + + AutoPtr tempXMP (host.Make_dng_xmp ()); + + tempXMP->Parse (host, buffer, count); + + ResetXMPSidecarNewer (tempXMP.Release (), xmpInSidecar, xmpIsNewer); + + result = true; + + } + + catch (dng_exception &except) + { + + // Don't ignore transient errors. + + if (host.IsTransientError (except.ErrorCode ())) + { + + throw; + + } + + // Eat other parsing errors. + + } + + catch (...) + { + + // Eat unknown parsing exceptions. + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_metadata::SetEmbeddedXMP (dng_host &host, + const void *buffer, + uint32 count) + { + + if (SetXMP (host, buffer, count)) + { + + dng_md5_printer printer; + + printer.Process (buffer, count); + + fEmbeddedXMPDigest = printer.Result (); + + // Remove any sidecar specific tags from embedded XMP. + + if (fXMP.Get ()) + { + + fXMP->Remove (XMP_NS_PHOTOSHOP, "SidecarForExtension"); + fXMP->Remove (XMP_NS_PHOTOSHOP, "EmbeddedXMPDigest"); + + } + + } + + else + { + + fEmbeddedXMPDigest.Clear (); + + } + + } + +/*****************************************************************************/ + +void dng_metadata::SynchronizeMetadata () + { + + if (!fOriginalExif.Get ()) + { + + fOriginalExif.Reset (fExif->Clone ()); + + } + + fXMP->ValidateMetadata (); + + fXMP->IngestIPTC (*this, fXMPisNewer); + + fXMP->SyncExif (*fExif.Get ()); + + fXMP->SyncOrientation (*this, fXMPinSidecar); + + } + +/*****************************************************************************/ + +void dng_metadata::UpdateDateTime (const dng_date_time_info &dt) + { + + fExif->UpdateDateTime (dt); + + fXMP->UpdateDateTime (dt); + + } + +/*****************************************************************************/ + +void dng_metadata::UpdateDateTimeToNow () + { + + dng_date_time_info dt; + + CurrentDateTimeAndZone (dt); + + UpdateDateTime (dt); + + fXMP->UpdateMetadataDate (dt); + + } + +/*****************************************************************************/ + +void dng_metadata::UpdateMetadataDateTimeToNow () + { + + dng_date_time_info dt; + + CurrentDateTimeAndZone (dt); + + fXMP->UpdateMetadataDate (dt); + + } + +/*****************************************************************************/ + +dng_negative::dng_negative (dng_host &host) + + : fAllocator (host.Allocator ()) + + , fModelName () + , fLocalName () + , fDefaultCropSizeH () + , fDefaultCropSizeV () + , fDefaultCropOriginH (0, 1) + , fDefaultCropOriginV (0, 1) + , fDefaultUserCropT (0, 1) + , fDefaultUserCropL (0, 1) + , fDefaultUserCropB (1, 1) + , fDefaultUserCropR (1, 1) + , fDefaultScaleH (1, 1) + , fDefaultScaleV (1, 1) + , fBestQualityScale (1, 1) + , fOriginalDefaultFinalSize () + , fOriginalBestQualityFinalSize () + , fOriginalDefaultCropSizeH () + , fOriginalDefaultCropSizeV () + , fRawToFullScaleH (1.0) + , fRawToFullScaleV (1.0) + , fBaselineNoise (100, 100) + , fNoiseReductionApplied (0, 0) + , fNoiseProfile () + , fBaselineExposure ( 0, 100) + , fBaselineSharpness (100, 100) + , fChromaBlurRadius () + , fAntiAliasStrength (100, 100) + , fLinearResponseLimit (100, 100) + , fShadowScale (1, 1) + , fColorimetricReference (crSceneReferred) + , fColorChannels (0) + , fAnalogBalance () + , fCameraNeutral () + , fCameraWhiteXY () + , fCameraCalibration1 () + , fCameraCalibration2 () + , fCameraCalibrationSignature () + , fCameraProfile () + , fAsShotProfileName () + , fRawImageDigest () + , fNewRawImageDigest () + , fRawDataUniqueID () + , fOriginalRawFileName () + , fHasOriginalRawFileData (false) + , fOriginalRawFileData () + , fOriginalRawFileDigest () + , fDNGPrivateData () + , fMetadata (host) + , fLinearizationInfo () + , fMosaicInfo () + , fOpcodeList1 (1) + , fOpcodeList2 (2) + , fOpcodeList3 (3) + , fStage1Image () + , fStage2Image () + , fStage3Image () + , fStage3Gain (1.0) + , fIsPreview (false) + , fIsDamaged (false) + , fRawImageStage (rawImageStageNone) + , fRawImage () + , fRawFloatBitDepth (0) + , fRawJPEGImage () + , fRawJPEGImageDigest () + , fTransparencyMask () + , fRawTransparencyMask () + , fRawTransparencyMaskBitDepth (0) + , fUnflattenedStage3Image () + + { + + } + +/*****************************************************************************/ + +dng_negative::~dng_negative () + { + + // Delete any camera profiles owned by this negative. + + ClearProfiles (); + + } + +/******************************************************************************/ + +void dng_negative::Initialize () + { + + } + +/******************************************************************************/ + +dng_negative * dng_negative::Make (dng_host &host) + { + + AutoPtr result (new dng_negative (host)); + + if (!result.Get ()) + { + ThrowMemoryFull (); + } + + result->Initialize (); + + return result.Release (); + + } + +/******************************************************************************/ + +dng_metadata * dng_negative::CloneInternalMetadata () const + { + + return InternalMetadata ().Clone (Allocator ()); + + } + +/******************************************************************************/ + +dng_orientation dng_negative::ComputeOrientation (const dng_metadata &metadata) const + { + + return metadata.BaseOrientation (); + + } + +/******************************************************************************/ + +void dng_negative::SetAnalogBalance (const dng_vector &b) + { + + real64 minEntry = b.MinEntry (); + + if (b.NotEmpty () && minEntry > 0.0) + { + + fAnalogBalance = b; + + fAnalogBalance.Scale (1.0 / minEntry); + + fAnalogBalance.Round (1000000.0); + + } + + else + { + + fAnalogBalance.Clear (); + + } + + } + +/*****************************************************************************/ + +real64 dng_negative::AnalogBalance (uint32 channel) const + { + + DNG_ASSERT (channel < ColorChannels (), "Channel out of bounds"); + + if (channel < fAnalogBalance.Count ()) + { + + return fAnalogBalance [channel]; + + } + + return 1.0; + + } + +/*****************************************************************************/ + +dng_urational dng_negative::AnalogBalanceR (uint32 channel) const + { + + dng_urational result; + + result.Set_real64 (AnalogBalance (channel), 1000000); + + return result; + + } + +/******************************************************************************/ + +void dng_negative::SetCameraNeutral (const dng_vector &n) + { + + real64 maxEntry = n.MaxEntry (); + + if (n.NotEmpty () && maxEntry > 0.0) + { + + fCameraNeutral = n; + + fCameraNeutral.Scale (1.0 / maxEntry); + + fCameraNeutral.Round (1000000.0); + + } + + else + { + + fCameraNeutral.Clear (); + + } + + } + +/*****************************************************************************/ + +dng_urational dng_negative::CameraNeutralR (uint32 channel) const + { + + dng_urational result; + + result.Set_real64 (CameraNeutral () [channel], 1000000); + + return result; + + } + +/******************************************************************************/ + +void dng_negative::SetCameraWhiteXY (const dng_xy_coord &coord) + { + + if (coord.IsValid ()) + { + + fCameraWhiteXY.x = Round_int32 (coord.x * 1000000.0) / 1000000.0; + fCameraWhiteXY.y = Round_int32 (coord.y * 1000000.0) / 1000000.0; + + } + + else + { + + fCameraWhiteXY.Clear (); + + } + + } + +/*****************************************************************************/ + +const dng_xy_coord & dng_negative::CameraWhiteXY () const + { + + DNG_ASSERT (HasCameraWhiteXY (), "Using undefined CameraWhiteXY"); + + return fCameraWhiteXY; + + } + +/*****************************************************************************/ + +void dng_negative::GetCameraWhiteXY (dng_urational &x, + dng_urational &y) const + { + + dng_xy_coord coord = CameraWhiteXY (); + + x.Set_real64 (coord.x, 1000000); + y.Set_real64 (coord.y, 1000000); + + } + +/*****************************************************************************/ + +void dng_negative::SetCameraCalibration1 (const dng_matrix &m) + { + + fCameraCalibration1 = m; + + fCameraCalibration1.Round (10000); + + } + +/******************************************************************************/ + +void dng_negative::SetCameraCalibration2 (const dng_matrix &m) + { + + fCameraCalibration2 = m; + + fCameraCalibration2.Round (10000); + + } + +/******************************************************************************/ + +void dng_negative::AddProfile (AutoPtr &profile) + { + + // Make sure we have a profile to add. + + if (!profile.Get ()) + { + + return; + + } + + // We must have some profile name. Use "embedded" if nothing else. + + if (profile->Name ().IsEmpty ()) + { + + profile->SetName (kProfileName_Embedded); + + } + + // Special case support for reading older DNG files which did not store + // the profile name in the main IFD profile. + + if (fCameraProfile.size ()) + { + + // See the first profile has a default "embedded" name, and has + // the same data as the profile we are adding. + + if (fCameraProfile [0]->NameIsEmbedded () && + fCameraProfile [0]->EqualData (*profile.Get ())) + { + + // If the profile we are deleting was read from DNG + // then the new profile should be marked as such also. + + if (fCameraProfile [0]->WasReadFromDNG ()) + { + + profile->SetWasReadFromDNG (); + + } + + // If the profile we are deleting wasn't read from disk then the new + // profile should be marked as such also. + + if (!fCameraProfile [0]->WasReadFromDisk ()) + { + + profile->SetWasReadFromDisk (false); + + } + + // Delete the profile with default name. + + delete fCameraProfile [0]; + + fCameraProfile [0] = NULL; + + fCameraProfile.erase (fCameraProfile.begin ()); + + } + + } + + // Duplicate detection logic. We give a preference to last added profile + // so the profiles end up in a more consistent order no matter what profiles + // happen to be embedded in the DNG. + + for (uint32 index = 0; index < (uint32) fCameraProfile.size (); index++) + { + + // Instead of checking for matching fingerprints, we check that the two + // profiles have the same color and have the same name. This allows two + // profiles that are identical except for copyright string and embed policy + // to be considered duplicates. + + const bool equalColorAndSameName = (fCameraProfile [index]->EqualData (*profile.Get ()) && + fCameraProfile [index]->Name () == profile->Name ()); + + if (equalColorAndSameName) + { + + // If the profile we are deleting was read from DNG + // then the new profile should be marked as such also. + + if (fCameraProfile [index]->WasReadFromDNG ()) + { + + profile->SetWasReadFromDNG (); + + } + + // If the profile we are deleting wasn't read from disk then the new + // profile should be marked as such also. + + if (!fCameraProfile [index]->WasReadFromDisk ()) + { + + profile->SetWasReadFromDisk (false); + + } + + // Delete the duplicate profile. + + delete fCameraProfile [index]; + + fCameraProfile [index] = NULL; + + fCameraProfile.erase (fCameraProfile.begin () + index); + + break; + + } + + } + + // Now add to profile list. + + fCameraProfile.push_back (NULL); + + fCameraProfile [fCameraProfile.size () - 1] = profile.Release (); + + } + +/******************************************************************************/ + +void dng_negative::ClearProfiles () + { + + // Delete any camera profiles owned by this negative. + + for (uint32 index = 0; index < (uint32) fCameraProfile.size (); index++) + { + + if (fCameraProfile [index]) + { + + delete fCameraProfile [index]; + + fCameraProfile [index] = NULL; + + } + + } + + // Now empty list. + + fCameraProfile.clear (); + + } + +/*****************************************************************************/ + +void dng_negative::ClearProfiles (bool clearBuiltinMatrixProfiles, + bool clearReadFromDisk) + { + + // If neither flag is set, then there's nothing to do. + + if (!clearBuiltinMatrixProfiles && + !clearReadFromDisk) + { + return; + } + + // Delete any camera profiles in this negative that match the specified criteria. + + std::vector::iterator iter = fCameraProfile.begin (); + std::vector::iterator next; + + for (; iter != fCameraProfile.end (); iter = next) + { + + dng_camera_profile *profile = *iter; + + // If the profile is invalid (i.e., NULL pointer), or meets one of the + // specified criteria, then axe it. + + if (!profile || + (clearBuiltinMatrixProfiles && profile->WasBuiltinMatrix ()) || + (clearReadFromDisk && profile->WasReadFromDisk ())) + { + + delete profile; + + next = fCameraProfile.erase (iter); + + } + + // Otherwise, just advance to the next element. + + else + { + + next = iter + 1; + + } + + } + + } + +/******************************************************************************/ + +uint32 dng_negative::ProfileCount () const + { + + return (uint32) fCameraProfile.size (); + + } + +/******************************************************************************/ + +const dng_camera_profile & dng_negative::ProfileByIndex (uint32 index) const + { + + DNG_ASSERT (index < ProfileCount (), + "Invalid index for ProfileByIndex"); + + return *fCameraProfile [index]; + + } + +/*****************************************************************************/ + +const dng_camera_profile * dng_negative::ProfileByID (const dng_camera_profile_id &id, + bool useDefaultIfNoMatch) const + { + + uint32 index; + + // If this negative does not have any profiles, we are not going to + // find a match. + + uint32 profileCount = ProfileCount (); + + if (profileCount == 0) + { + return NULL; + } + + // If we have both a profile name and fingerprint, try matching both. + + if (id.Name ().NotEmpty () && id.Fingerprint ().IsValid ()) + { + + for (index = 0; index < profileCount; index++) + { + + const dng_camera_profile &profile = ProfileByIndex (index); + + if (id.Name () == profile.Name () && + id.Fingerprint () == profile.Fingerprint ()) + { + + return &profile; + + } + + } + + } + + // If we have a name, try matching that. + + if (id.Name ().NotEmpty ()) + { + + for (index = 0; index < profileCount; index++) + { + + const dng_camera_profile &profile = ProfileByIndex (index); + + if (id.Name () == profile.Name ()) + { + + return &profile; + + } + + } + + } + + // If we have a valid fingerprint, try matching that. + + if (id.Fingerprint ().IsValid ()) + { + + for (index = 0; index < profileCount; index++) + { + + const dng_camera_profile &profile = ProfileByIndex (index); + + if (id.Fingerprint () == profile.Fingerprint ()) + { + + return &profile; + + } + + } + + } + + // Try "upgrading" profile name versions. + + if (id.Name ().NotEmpty ()) + { + + dng_string baseName; + int32 version; + + SplitCameraProfileName (id.Name (), + baseName, + version); + + int32 bestIndex = -1; + int32 bestVersion = 0; + + for (index = 0; index < profileCount; index++) + { + + const dng_camera_profile &profile = ProfileByIndex (index); + + if (profile.Name ().StartsWith (baseName.Get ())) + { + + dng_string testBaseName; + int32 testVersion; + + SplitCameraProfileName (profile.Name (), + testBaseName, + testVersion); + + if (bestIndex == -1 || testVersion > bestVersion) + { + + bestIndex = index; + bestVersion = testVersion; + + } + + } + + } + + if (bestIndex != -1) + { + + return &ProfileByIndex (bestIndex); + + } + + } + + // Did not find a match any way. See if we should return a default value. + + if (useDefaultIfNoMatch) + { + + return &ProfileByIndex (0); + + } + + // Found nothing. + + return NULL; + + } + +/*****************************************************************************/ + +const dng_camera_profile * dng_negative::ComputeCameraProfileToEmbed + (const dng_metadata & /* metadata */) const + { + + uint32 index; + + uint32 count = ProfileCount (); + + if (count == 0) + { + + return NULL; + + } + + // First try to look for the first profile that was already in the DNG + // when we read it. + + for (index = 0; index < count; index++) + { + + const dng_camera_profile &profile (ProfileByIndex (index)); + + if (profile.WasReadFromDNG ()) + { + + return &profile; + + } + + } + + // Next we look for the first profile that is legal to embed. + + for (index = 0; index < count; index++) + { + + const dng_camera_profile &profile (ProfileByIndex (index)); + + if (profile.IsLegalToEmbed ()) + { + + return &profile; + + } + + } + + // Else just return the first profile. + + return fCameraProfile [0]; + + } + +/*****************************************************************************/ + +dng_color_spec * dng_negative::MakeColorSpec (const dng_camera_profile_id &id) const + { + + dng_color_spec *spec = new dng_color_spec (*this, ProfileByID (id)); + + if (!spec) + { + ThrowMemoryFull (); + } + + return spec; + + } + +/*****************************************************************************/ + +dng_fingerprint dng_negative::FindImageDigest (dng_host &host, + const dng_image &image) const + { + + dng_md5_printer printer; + + dng_pixel_buffer buffer; + + buffer.fPlane = 0; + buffer.fPlanes = image.Planes (); + + buffer.fRowStep = image.Planes () * image.Width (); + buffer.fColStep = image.Planes (); + buffer.fPlaneStep = 1; + + buffer.fPixelType = image.PixelType (); + buffer.fPixelSize = image.PixelSize (); + + // Sometimes we expand 8-bit data to 16-bit data while reading or + // writing, so always compute the digest of 8-bit data as 16-bits. + + if (buffer.fPixelType == ttByte) + { + buffer.fPixelType = ttShort; + buffer.fPixelSize = 2; + } + + const uint32 kBufferRows = 16; + + uint32 bufferBytes = kBufferRows * buffer.fRowStep * buffer.fPixelSize; + + AutoPtr bufferData (host.Allocate (bufferBytes)); + + buffer.fData = bufferData->Buffer (); + + dng_rect area; + + dng_tile_iterator iter (dng_point (kBufferRows, + image.Width ()), + image.Bounds ()); + + while (iter.GetOneTile (area)) + { + + host.SniffForAbort (); + + buffer.fArea = area; + + image.Get (buffer); + + uint32 count = buffer.fArea.H () * + buffer.fRowStep * + buffer.fPixelSize; + + #if qDNGBigEndian + + // We need to use the same byte order to compute + // the digest, no matter the native order. Little-endian + // is more common now, so use that. + + switch (buffer.fPixelSize) + { + + case 1: + break; + + case 2: + { + DoSwapBytes16 ((uint16 *) buffer.fData, count >> 1); + break; + } + + case 4: + { + DoSwapBytes32 ((uint32 *) buffer.fData, count >> 2); + break; + } + + default: + { + DNG_REPORT ("Unexpected pixel size"); + break; + } + + } + + #endif + + printer.Process (buffer.fData, + count); + + } + + return printer.Result (); + + } + +/*****************************************************************************/ + +void dng_negative::FindRawImageDigest (dng_host &host) const + { + + if (fRawImageDigest.IsNull ()) + { + + // Since we are adding the floating point and transparency support + // in DNG 1.4, and there are no legacy floating point or transparent + // DNGs, switch to using the more MP friendly algorithm to compute + // the digest for these images. + + if (RawImage ().PixelType () == ttFloat || RawTransparencyMask ()) + { + + FindNewRawImageDigest (host); + + fRawImageDigest = fNewRawImageDigest; + + } + + else + { + + #if qDNGValidate + + dng_timer timeScope ("FindRawImageDigest time"); + + #endif + + fRawImageDigest = FindImageDigest (host, RawImage ()); + + } + + } + + } + +/*****************************************************************************/ + +class dng_find_new_raw_image_digest_task : public dng_area_task + { + + private: + + enum + { + kTileSize = 256 + }; + + const dng_image &fImage; + + uint32 fPixelType; + uint32 fPixelSize; + + uint32 fTilesAcross; + uint32 fTilesDown; + + uint32 fTileCount; + + AutoArray fTileHash; + + AutoPtr fBufferData [kMaxMPThreads]; + + public: + + dng_find_new_raw_image_digest_task (const dng_image &image, + uint32 pixelType) + + : fImage (image) + , fPixelType (pixelType) + , fPixelSize (TagTypeSize (pixelType)) + , fTilesAcross (0) + , fTilesDown (0) + , fTileCount (0) + , fTileHash (NULL) + + { + + fMinTaskArea = 1; + + fUnitCell = dng_point (Min_int32 (kTileSize, fImage.Bounds ().H ()), + Min_int32 (kTileSize, fImage.Bounds ().W ())); + + fMaxTileSize = fUnitCell; + + } + + virtual void Start (uint32 threadCount, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer * /* sniffer */) + { + + if (tileSize != fUnitCell) + { + ThrowProgramError (); + } + + fTilesAcross = (fImage.Bounds ().W () + fUnitCell.h - 1) / fUnitCell.h; + fTilesDown = (fImage.Bounds ().H () + fUnitCell.v - 1) / fUnitCell.v; + + fTileCount = fTilesAcross * fTilesDown; + + fTileHash.Reset (new dng_fingerprint [fTileCount]); + + uint32 bufferSize = fImage.Planes () * + fPixelSize * + tileSize.h * + tileSize.v; + + for (uint32 index = 0; index < threadCount; index++) + { + + fBufferData [index].Reset (allocator->Allocate (bufferSize)); + + } + + } + + virtual void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer * /* sniffer */) + { + + int32 colIndex = (tile.l - fImage.Bounds ().l) / fUnitCell.h; + int32 rowIndex = (tile.t - fImage.Bounds ().t) / fUnitCell.v; + + DNG_ASSERT (tile.l == fImage.Bounds ().l + colIndex * fUnitCell.h && + tile.t == fImage.Bounds ().t + rowIndex * fUnitCell.v, + "Bad tile origin"); + + uint32 tileIndex = rowIndex * fTilesAcross + colIndex; + + dng_pixel_buffer buffer; + + buffer.fArea = tile; + + buffer.fPlane = 0; + buffer.fPlanes = fImage.Planes (); + + buffer.fRowStep = tile.W (); + buffer.fColStep = 1; + buffer.fPlaneStep = tile.W () * tile.H (); + + buffer.fPixelType = fPixelType; + buffer.fPixelSize = fPixelSize; + + buffer.fData = fBufferData [threadIndex]->Buffer (); + + fImage.Get (buffer); + + uint32 count = buffer.fPlaneStep * + buffer.fPlanes * + buffer.fPixelSize; + + #if qDNGBigEndian + + // We need to use the same byte order to compute + // the digest, no matter the native order. Little-endian + // is more common now, so use that. + + switch (buffer.fPixelSize) + { + + case 1: + break; + + case 2: + { + DoSwapBytes16 ((uint16 *) buffer.fData, count >> 1); + break; + } + + case 4: + { + DoSwapBytes32 ((uint32 *) buffer.fData, count >> 2); + break; + } + + default: + { + DNG_REPORT ("Unexpected pixel size"); + break; + } + + } + + #endif + + dng_md5_printer printer; + + printer.Process (buffer.fData, count); + + fTileHash [tileIndex] = printer.Result (); + + } + + dng_fingerprint Result () + { + + dng_md5_printer printer; + + for (uint32 tileIndex = 0; tileIndex < fTileCount; tileIndex++) + { + + printer.Process (fTileHash [tileIndex] . data, 16); + + } + + return printer.Result (); + + } + + }; + +/*****************************************************************************/ + +void dng_negative::FindNewRawImageDigest (dng_host &host) const + { + + if (fNewRawImageDigest.IsNull ()) + { + + #if qDNGValidate + + dng_timer timeScope ("FindNewRawImageDigest time"); + + #endif + + // Find fast digest of the raw image. + + { + + const dng_image &rawImage = RawImage (); + + // Find pixel type that will be saved in the file. When saving DNGs, we convert + // some 16-bit data to 8-bit data, so we need to do the matching logic here. + + uint32 rawPixelType = rawImage.PixelType (); + + if (rawPixelType == ttShort) + { + + // See if we are using a linearization table with <= 256 entries, in which + // case the useful data will all fit within 8-bits. + + const dng_linearization_info *rangeInfo = GetLinearizationInfo (); + + if (rangeInfo) + { + + if (rangeInfo->fLinearizationTable.Get ()) + { + + uint32 entries = rangeInfo->fLinearizationTable->LogicalSize () >> 1; + + if (entries <= 256) + { + + rawPixelType = ttByte; + + } + + } + + } + + } + + // Find the fast digest on the raw image. + + dng_find_new_raw_image_digest_task task (rawImage, rawPixelType); + + host.PerformAreaTask (task, rawImage.Bounds ()); + + fNewRawImageDigest = task.Result (); + + } + + // If there is a transparancy mask, we need to include that in the + // digest also. + + if (RawTransparencyMask () != NULL) + { + + // Find the fast digest on the raw mask. + + dng_fingerprint maskDigest; + + { + + dng_find_new_raw_image_digest_task task (*RawTransparencyMask (), + RawTransparencyMask ()->PixelType ()); + + host.PerformAreaTask (task, RawTransparencyMask ()->Bounds ()); + + maskDigest = task.Result (); + + } + + // Combine the two digests into a single digest. + + dng_md5_printer printer; + + printer.Process (fNewRawImageDigest.data, 16); + + printer.Process (maskDigest.data, 16); + + fNewRawImageDigest = printer.Result (); + + } + + } + + } + +/*****************************************************************************/ + +void dng_negative::ValidateRawImageDigest (dng_host &host) + { + + if (Stage1Image () && !IsPreview () && (fRawImageDigest .IsValid () || + fNewRawImageDigest.IsValid ())) + { + + bool isNewDigest = fNewRawImageDigest.IsValid (); + + dng_fingerprint &rawDigest = isNewDigest ? fNewRawImageDigest + : fRawImageDigest; + + // For lossy compressed JPEG images, we need to compare the stored + // digest to the digest computed from the compressed data, since + // decompressing lossy JPEG data is itself a lossy process. + + if (RawJPEGImageDigest ().IsValid () || RawJPEGImage ()) + { + + // Compute the raw JPEG image digest if we have not done so + // already. + + FindRawJPEGImageDigest (host); + + if (rawDigest != RawJPEGImageDigest ()) + { + + #if qDNGValidate + + ReportError ("RawImageDigest does not match raw jpeg image"); + + #else + + SetIsDamaged (true); + + #endif + + } + + } + + // Else we can compare the stored digest to the image in memory. + + else + { + + dng_fingerprint oldDigest = rawDigest; + + try + { + + rawDigest.Clear (); + + if (isNewDigest) + { + + FindNewRawImageDigest (host); + + } + + else + { + + FindRawImageDigest (host); + + } + + } + + catch (...) + { + + rawDigest = oldDigest; + + throw; + + } + + if (oldDigest != rawDigest) + { + + #if qDNGValidate + + if (isNewDigest) + { + ReportError ("NewRawImageDigest does not match raw image"); + } + else + { + ReportError ("RawImageDigest does not match raw image"); + } + + SetIsDamaged (true); + + #else + + if (!isNewDigest) + { + + // Note that Lightroom 1.4 Windows had a bug that corrupts the + // first four bytes of the RawImageDigest tag. So if the last + // twelve bytes match, this is very likely the result of the + // bug, and not an actual corrupt file. So don't report this + // to the user--just fix it. + + { + + bool matchLast12 = true; + + for (uint32 j = 4; j < 16; j++) + { + matchLast12 = matchLast12 && (oldDigest.data [j] == fRawImageDigest.data [j]); + } + + if (matchLast12) + { + return; + } + + } + + // Sometimes Lightroom 1.4 would corrupt more than the first four + // bytes, but for all those files that I have seen so far the + // resulting first four bytes are 0x08 0x00 0x00 0x00. + + if (oldDigest.data [0] == 0x08 && + oldDigest.data [1] == 0x00 && + oldDigest.data [2] == 0x00 && + oldDigest.data [3] == 0x00) + { + return; + } + + } + + SetIsDamaged (true); + + #endif + + } + + } + + } + + } + +/*****************************************************************************/ + +// If the raw data unique ID is missing, compute one based on a MD5 hash of +// the raw image hash and the model name, plus other commonly changed +// data that can affect rendering. + +void dng_negative::FindRawDataUniqueID (dng_host &host) const + { + + if (fRawDataUniqueID.IsNull ()) + { + + dng_md5_printer_stream printer; + + // If we have a raw jpeg image, it is much faster to + // use its digest as part of the unique ID since + // the data size is much smaller. We cannot use it + // if there a transparency mask, since that is not + // included in the RawJPEGImageDigest. + + if (RawJPEGImage () && !RawTransparencyMask ()) + { + + FindRawJPEGImageDigest (host); + + printer.Put (fRawJPEGImageDigest.data, 16); + + } + + // Include the new raw image digest in the unique ID. + + else + { + + FindNewRawImageDigest (host); + + printer.Put (fNewRawImageDigest.data, 16); + + } + + // Include model name. + + printer.Put (ModelName ().Get (), + ModelName ().Length ()); + + // Include default crop area, since DNG Recover Edges can modify + // these values and they affect rendering. + + printer.Put_uint32 (fDefaultCropSizeH.n); + printer.Put_uint32 (fDefaultCropSizeH.d); + + printer.Put_uint32 (fDefaultCropSizeV.n); + printer.Put_uint32 (fDefaultCropSizeV.d); + + printer.Put_uint32 (fDefaultCropOriginH.n); + printer.Put_uint32 (fDefaultCropOriginH.d); + + printer.Put_uint32 (fDefaultCropOriginV.n); + printer.Put_uint32 (fDefaultCropOriginV.d); + + // Include default user crop. + + printer.Put_uint32 (fDefaultUserCropT.n); + printer.Put_uint32 (fDefaultUserCropT.d); + + printer.Put_uint32 (fDefaultUserCropL.n); + printer.Put_uint32 (fDefaultUserCropL.d); + + printer.Put_uint32 (fDefaultUserCropB.n); + printer.Put_uint32 (fDefaultUserCropB.d); + + printer.Put_uint32 (fDefaultUserCropR.n); + printer.Put_uint32 (fDefaultUserCropR.d); + + // Include opcode lists, since lens correction utilities can modify + // these values and they affect rendering. + + fOpcodeList1.FingerprintToStream (printer); + fOpcodeList2.FingerprintToStream (printer); + fOpcodeList3.FingerprintToStream (printer); + + fRawDataUniqueID = printer.Result (); + + } + + } + +/******************************************************************************/ + +// Forces recomputation of RawDataUniqueID, useful to call +// after modifying the opcode lists, etc. + +void dng_negative::RecomputeRawDataUniqueID (dng_host &host) + { + + fRawDataUniqueID.Clear (); + + FindRawDataUniqueID (host); + + } + +/******************************************************************************/ + +void dng_negative::FindOriginalRawFileDigest () const + { + + if (fOriginalRawFileDigest.IsNull () && fOriginalRawFileData.Get ()) + { + + dng_md5_printer printer; + + printer.Process (fOriginalRawFileData->Buffer (), + fOriginalRawFileData->LogicalSize ()); + + fOriginalRawFileDigest = printer.Result (); + + } + + } + +/*****************************************************************************/ + +void dng_negative::ValidateOriginalRawFileDigest () + { + + if (fOriginalRawFileDigest.IsValid () && fOriginalRawFileData.Get ()) + { + + dng_fingerprint oldDigest = fOriginalRawFileDigest; + + try + { + + fOriginalRawFileDigest.Clear (); + + FindOriginalRawFileDigest (); + + } + + catch (...) + { + + fOriginalRawFileDigest = oldDigest; + + throw; + + } + + if (oldDigest != fOriginalRawFileDigest) + { + + #if qDNGValidate + + ReportError ("OriginalRawFileDigest does not match OriginalRawFileData"); + + #else + + SetIsDamaged (true); + + #endif + + // Don't "repair" the original image data digest. Once it is + // bad, it stays bad. The user cannot tell by looking at the image + // whether the damage is acceptable and can be ignored in the + // future. + + fOriginalRawFileDigest = oldDigest; + + } + + } + + } + +/******************************************************************************/ + +dng_rect dng_negative::DefaultCropArea () const + { + + // First compute the area using simple rounding. + + dng_rect result; + + result.l = Round_int32 (fDefaultCropOriginH.As_real64 () * fRawToFullScaleH); + result.t = Round_int32 (fDefaultCropOriginV.As_real64 () * fRawToFullScaleV); + + result.r = result.l + Round_int32 (fDefaultCropSizeH.As_real64 () * fRawToFullScaleH); + result.b = result.t + Round_int32 (fDefaultCropSizeV.As_real64 () * fRawToFullScaleV); + + // Sometimes the simple rounding causes the resulting default crop + // area to slide off the scaled image area. So we force this not + // to happen. We only do this if the image is not stubbed. + + const dng_image *image = Stage3Image (); + + if (image) + { + + dng_point imageSize = image->Size (); + + if (result.r > imageSize.h) + { + result.l -= result.r - imageSize.h; + result.r = imageSize.h; + } + + if (result.b > imageSize.v) + { + result.t -= result.b - imageSize.v; + result.b = imageSize.v; + } + + } + + return result; + + } + +/*****************************************************************************/ + +real64 dng_negative::TotalBaselineExposure (const dng_camera_profile_id &profileID) const + { + + real64 total = BaselineExposure (); + + const dng_camera_profile *profile = ProfileByID (profileID); + + if (profile) + { + + real64 offset = profile->BaselineExposureOffset ().As_real64 (); + + total += offset; + + } + + return total; + + } + +/******************************************************************************/ + +void dng_negative::SetShadowScale (const dng_urational &scale) + { + + if (scale.d > 0) + { + + real64 s = scale.As_real64 (); + + if (s > 0.0 && s <= 1.0) + { + + fShadowScale = scale; + + } + + } + + } + +/******************************************************************************/ + +void dng_negative::SetActiveArea (const dng_rect &area) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fActiveArea = area; + + } + +/******************************************************************************/ + +void dng_negative::SetMaskedAreas (uint32 count, + const dng_rect *area) + { + + DNG_ASSERT (count <= kMaxMaskedAreas, "Too many masked areas"); + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fMaskedAreaCount = Min_uint32 (count, kMaxMaskedAreas); + + for (uint32 index = 0; index < info.fMaskedAreaCount; index++) + { + + info.fMaskedArea [index] = area [index]; + + } + + } + +/*****************************************************************************/ + +void dng_negative::SetLinearization (AutoPtr &curve) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fLinearizationTable.Reset (curve.Release ()); + + } + +/*****************************************************************************/ + +void dng_negative::SetBlackLevel (real64 black, + int32 plane) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fBlackLevelRepeatRows = 1; + info.fBlackLevelRepeatCols = 1; + + if (plane < 0) + { + + for (uint32 j = 0; j < kMaxSamplesPerPixel; j++) + { + + info.fBlackLevel [0] [0] [j] = black; + + } + + } + + else + { + + info.fBlackLevel [0] [0] [plane] = black; + + } + + info.RoundBlacks (); + + } + +/*****************************************************************************/ + +void dng_negative::SetQuadBlacks (real64 black0, + real64 black1, + real64 black2, + real64 black3, + int32 plane) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fBlackLevelRepeatRows = 2; + info.fBlackLevelRepeatCols = 2; + + if (plane < 0) + { + + for (uint32 j = 0; j < kMaxSamplesPerPixel; j++) + { + + info.fBlackLevel [0] [0] [j] = black0; + info.fBlackLevel [0] [1] [j] = black1; + info.fBlackLevel [1] [0] [j] = black2; + info.fBlackLevel [1] [1] [j] = black3; + + } + + } + + else + { + + info.fBlackLevel [0] [0] [plane] = black0; + info.fBlackLevel [0] [1] [plane] = black1; + info.fBlackLevel [1] [0] [plane] = black2; + info.fBlackLevel [1] [1] [plane] = black3; + + } + + info.RoundBlacks (); + + } + +/*****************************************************************************/ + +void dng_negative::SetRowBlacks (const real64 *blacks, + uint32 count) + { + + if (count) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + uint32 byteCount = count * (uint32) sizeof (real64); + + info.fBlackDeltaV.Reset (Allocator ().Allocate (byteCount)); + + DoCopyBytes (blacks, + info.fBlackDeltaV->Buffer (), + byteCount); + + info.RoundBlacks (); + + } + + else if (fLinearizationInfo.Get ()) + { + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fBlackDeltaV.Reset (); + + } + + } + +/*****************************************************************************/ + +void dng_negative::SetColumnBlacks (const real64 *blacks, + uint32 count) + { + + if (count) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + uint32 byteCount = count * (uint32) sizeof (real64); + + info.fBlackDeltaH.Reset (Allocator ().Allocate (byteCount)); + + DoCopyBytes (blacks, + info.fBlackDeltaH->Buffer (), + byteCount); + + info.RoundBlacks (); + + } + + else if (fLinearizationInfo.Get ()) + { + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.fBlackDeltaH.Reset (); + + } + + } + +/*****************************************************************************/ + +uint32 dng_negative::WhiteLevel (uint32 plane) const + { + + if (fLinearizationInfo.Get ()) + { + + const dng_linearization_info &info = *fLinearizationInfo.Get (); + + return Round_uint32 (info.fWhiteLevel [plane]); + + } + + if (RawImage ().PixelType () == ttFloat) + { + + return 1; + + } + + return 0x0FFFF; + + } + +/*****************************************************************************/ + +void dng_negative::SetWhiteLevel (uint32 white, + int32 plane) + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + if (plane < 0) + { + + for (uint32 j = 0; j < kMaxSamplesPerPixel; j++) + { + + info.fWhiteLevel [j] = (real64) white; + + } + + } + + else + { + + info.fWhiteLevel [plane] = (real64) white; + + } + + } + +/******************************************************************************/ + +void dng_negative::SetColorKeys (ColorKeyCode color0, + ColorKeyCode color1, + ColorKeyCode color2, + ColorKeyCode color3) + { + + NeedMosaicInfo (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + info.fCFAPlaneColor [0] = color0; + info.fCFAPlaneColor [1] = color1; + info.fCFAPlaneColor [2] = color2; + info.fCFAPlaneColor [3] = color3; + + } + +/******************************************************************************/ + +void dng_negative::SetBayerMosaic (uint32 phase) + { + + NeedMosaicInfo (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + ColorKeyCode color0 = (ColorKeyCode) info.fCFAPlaneColor [0]; + ColorKeyCode color1 = (ColorKeyCode) info.fCFAPlaneColor [1]; + ColorKeyCode color2 = (ColorKeyCode) info.fCFAPlaneColor [2]; + + info.fCFAPatternSize = dng_point (2, 2); + + switch (phase) + { + + case 0: + { + info.fCFAPattern [0] [0] = color1; + info.fCFAPattern [0] [1] = color0; + info.fCFAPattern [1] [0] = color2; + info.fCFAPattern [1] [1] = color1; + break; + } + + case 1: + { + info.fCFAPattern [0] [0] = color0; + info.fCFAPattern [0] [1] = color1; + info.fCFAPattern [1] [0] = color1; + info.fCFAPattern [1] [1] = color2; + break; + } + + case 2: + { + info.fCFAPattern [0] [0] = color2; + info.fCFAPattern [0] [1] = color1; + info.fCFAPattern [1] [0] = color1; + info.fCFAPattern [1] [1] = color0; + break; + } + + case 3: + { + info.fCFAPattern [0] [0] = color1; + info.fCFAPattern [0] [1] = color2; + info.fCFAPattern [1] [0] = color0; + info.fCFAPattern [1] [1] = color1; + break; + } + + } + + info.fColorPlanes = 3; + + info.fCFALayout = 1; + + } + +/******************************************************************************/ + +void dng_negative::SetFujiMosaic (uint32 phase) + { + + NeedMosaicInfo (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + ColorKeyCode color0 = (ColorKeyCode) info.fCFAPlaneColor [0]; + ColorKeyCode color1 = (ColorKeyCode) info.fCFAPlaneColor [1]; + ColorKeyCode color2 = (ColorKeyCode) info.fCFAPlaneColor [2]; + + info.fCFAPatternSize = dng_point (2, 4); + + switch (phase) + { + + case 0: + { + info.fCFAPattern [0] [0] = color0; + info.fCFAPattern [0] [1] = color1; + info.fCFAPattern [0] [2] = color2; + info.fCFAPattern [0] [3] = color1; + info.fCFAPattern [1] [0] = color2; + info.fCFAPattern [1] [1] = color1; + info.fCFAPattern [1] [2] = color0; + info.fCFAPattern [1] [3] = color1; + break; + } + + case 1: + { + info.fCFAPattern [0] [0] = color2; + info.fCFAPattern [0] [1] = color1; + info.fCFAPattern [0] [2] = color0; + info.fCFAPattern [0] [3] = color1; + info.fCFAPattern [1] [0] = color0; + info.fCFAPattern [1] [1] = color1; + info.fCFAPattern [1] [2] = color2; + info.fCFAPattern [1] [3] = color1; + break; + } + + } + + info.fColorPlanes = 3; + + info.fCFALayout = 2; + + } + +/*****************************************************************************/ + +void dng_negative::SetFujiMosaic6x6 (uint32 phase) + { + + NeedMosaicInfo (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + ColorKeyCode color0 = (ColorKeyCode) info.fCFAPlaneColor [0]; + ColorKeyCode color1 = (ColorKeyCode) info.fCFAPlaneColor [1]; + ColorKeyCode color2 = (ColorKeyCode) info.fCFAPlaneColor [2]; + + const uint32 patSize = 6; + + info.fCFAPatternSize = dng_point (patSize, patSize); + + info.fCFAPattern [0] [0] = color1; + info.fCFAPattern [0] [1] = color2; + info.fCFAPattern [0] [2] = color1; + info.fCFAPattern [0] [3] = color1; + info.fCFAPattern [0] [4] = color0; + info.fCFAPattern [0] [5] = color1; + + info.fCFAPattern [1] [0] = color0; + info.fCFAPattern [1] [1] = color1; + info.fCFAPattern [1] [2] = color0; + info.fCFAPattern [1] [3] = color2; + info.fCFAPattern [1] [4] = color1; + info.fCFAPattern [1] [5] = color2; + + info.fCFAPattern [2] [0] = color1; + info.fCFAPattern [2] [1] = color2; + info.fCFAPattern [2] [2] = color1; + info.fCFAPattern [2] [3] = color1; + info.fCFAPattern [2] [4] = color0; + info.fCFAPattern [2] [5] = color1; + + info.fCFAPattern [3] [0] = color1; + info.fCFAPattern [3] [1] = color0; + info.fCFAPattern [3] [2] = color1; + info.fCFAPattern [3] [3] = color1; + info.fCFAPattern [3] [4] = color2; + info.fCFAPattern [3] [5] = color1; + + info.fCFAPattern [4] [0] = color2; + info.fCFAPattern [4] [1] = color1; + info.fCFAPattern [4] [2] = color2; + info.fCFAPattern [4] [3] = color0; + info.fCFAPattern [4] [4] = color1; + info.fCFAPattern [4] [5] = color0; + + info.fCFAPattern [5] [0] = color1; + info.fCFAPattern [5] [1] = color0; + info.fCFAPattern [5] [2] = color1; + info.fCFAPattern [5] [3] = color1; + info.fCFAPattern [5] [4] = color2; + info.fCFAPattern [5] [5] = color1; + + DNG_REQUIRE (phase >= 0 && phase < patSize * patSize, + "Bad phase in SetFujiMosaic6x6."); + + if (phase > 0) + { + + dng_mosaic_info temp = info; + + uint32 phaseRow = phase / patSize; + + uint32 phaseCol = phase - (phaseRow * patSize); + + for (uint32 dstRow = 0; dstRow < patSize; dstRow++) + { + + uint32 srcRow = (dstRow + phaseRow) % patSize; + + for (uint32 dstCol = 0; dstCol < patSize; dstCol++) + { + + uint32 srcCol = (dstCol + phaseCol) % patSize; + + temp.fCFAPattern [dstRow] [dstCol] = info.fCFAPattern [srcRow] [srcCol]; + + } + + } + + info = temp; + + } + + info.fColorPlanes = 3; + + info.fCFALayout = 1; + + } + +/******************************************************************************/ + +void dng_negative::SetQuadMosaic (uint32 pattern) + { + + // The pattern of the four colors is assumed to be repeat at least every two + // columns and eight rows. The pattern is encoded as a 32-bit integer, + // with every two bits encoding a color, in scan order for two columns and + // eight rows (lsb is first). The usual color coding is: + // + // 0 = Green + // 1 = Magenta + // 2 = Cyan + // 3 = Yellow + // + // Examples: + // + // PowerShot 600 uses 0xe1e4e1e4: + // + // 0 1 2 3 4 5 + // 0 G M G M G M + // 1 C Y C Y C Y + // 2 M G M G M G + // 3 C Y C Y C Y + // + // PowerShot A5 uses 0x1e4e1e4e: + // + // 0 1 2 3 4 5 + // 0 C Y C Y C Y + // 1 G M G M G M + // 2 C Y C Y C Y + // 3 M G M G M G + // + // PowerShot A50 uses 0x1b4e4b1e: + // + // 0 1 2 3 4 5 + // 0 C Y C Y C Y + // 1 M G M G M G + // 2 Y C Y C Y C + // 3 G M G M G M + // 4 C Y C Y C Y + // 5 G M G M G M + // 6 Y C Y C Y C + // 7 M G M G M G + // + // PowerShot Pro70 uses 0x1e4b4e1b: + // + // 0 1 2 3 4 5 + // 0 Y C Y C Y C + // 1 M G M G M G + // 2 C Y C Y C Y + // 3 G M G M G M + // 4 Y C Y C Y C + // 5 G M G M G M + // 6 C Y C Y C Y + // 7 M G M G M G + // + // PowerShots Pro90 and G1 use 0xb4b4b4b4: + // + // 0 1 2 3 4 5 + // 0 G M G M G M + // 1 Y C Y C Y C + + NeedMosaicInfo (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + if (((pattern >> 16) & 0x0FFFF) != (pattern & 0x0FFFF)) + { + info.fCFAPatternSize = dng_point (8, 2); + } + + else if (((pattern >> 8) & 0x0FF) != (pattern & 0x0FF)) + { + info.fCFAPatternSize = dng_point (4, 2); + } + + else + { + info.fCFAPatternSize = dng_point (2, 2); + } + + for (int32 row = 0; row < info.fCFAPatternSize.v; row++) + { + + for (int32 col = 0; col < info.fCFAPatternSize.h; col++) + { + + uint32 index = (pattern >> ((((row << 1) & 14) + (col & 1)) << 1)) & 3; + + info.fCFAPattern [row] [col] = info.fCFAPlaneColor [index]; + + } + + } + + info.fColorPlanes = 4; + + info.fCFALayout = 1; + + } + +/******************************************************************************/ + +void dng_negative::SetGreenSplit (uint32 split) + { + + NeedMosaicInfo (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + info.fBayerGreenSplit = split; + + } + +/*****************************************************************************/ + +void dng_negative::Parse (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + // Shared info. + + dng_shared &shared = *(info.fShared.Get ()); + + // Find IFD holding the main raw information. + + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex].Get (); + + // Model name. + + SetModelName (shared.fUniqueCameraModel.Get ()); + + // Localized model name. + + SetLocalName (shared.fLocalizedCameraModel.Get ()); + + // Base orientation. + + { + + uint32 orientation = info.fIFD [0]->fOrientation; + + if (orientation >= 1 && orientation <= 8) + { + + SetBaseOrientation (dng_orientation::TIFFtoDNG (orientation)); + + } + + } + + // Default crop rectangle. + + SetDefaultCropSize (rawIFD.fDefaultCropSizeH, + rawIFD.fDefaultCropSizeV); + + SetDefaultCropOrigin (rawIFD.fDefaultCropOriginH, + rawIFD.fDefaultCropOriginV); + + // Default user crop rectangle. + + SetDefaultUserCrop (rawIFD.fDefaultUserCropT, + rawIFD.fDefaultUserCropL, + rawIFD.fDefaultUserCropB, + rawIFD.fDefaultUserCropR); + + // Default scale. + + SetDefaultScale (rawIFD.fDefaultScaleH, + rawIFD.fDefaultScaleV); + + // Best quality scale. + + SetBestQualityScale (rawIFD.fBestQualityScale); + + // Baseline noise. + + SetBaselineNoise (shared.fBaselineNoise.As_real64 ()); + + // NoiseReductionApplied. + + SetNoiseReductionApplied (shared.fNoiseReductionApplied); + + // NoiseProfile. + + SetNoiseProfile (shared.fNoiseProfile); + + // Baseline exposure. + + SetBaselineExposure (shared.fBaselineExposure.As_real64 ()); + + // Baseline sharpness. + + SetBaselineSharpness (shared.fBaselineSharpness.As_real64 ()); + + // Chroma blur radius. + + SetChromaBlurRadius (rawIFD.fChromaBlurRadius); + + // Anti-alias filter strength. + + SetAntiAliasStrength (rawIFD.fAntiAliasStrength); + + // Linear response limit. + + SetLinearResponseLimit (shared.fLinearResponseLimit.As_real64 ()); + + // Shadow scale. + + SetShadowScale (shared.fShadowScale); + + // Colorimetric reference. + + SetColorimetricReference (shared.fColorimetricReference); + + // Color channels. + + SetColorChannels (shared.fCameraProfile.fColorPlanes); + + // Analog balance. + + if (shared.fAnalogBalance.NotEmpty ()) + { + + SetAnalogBalance (shared.fAnalogBalance); + + } + + // Camera calibration matrices + + if (shared.fCameraCalibration1.NotEmpty ()) + { + + SetCameraCalibration1 (shared.fCameraCalibration1); + + } + + if (shared.fCameraCalibration2.NotEmpty ()) + { + + SetCameraCalibration2 (shared.fCameraCalibration2); + + } + + if (shared.fCameraCalibration1.NotEmpty () || + shared.fCameraCalibration2.NotEmpty ()) + { + + SetCameraCalibrationSignature (shared.fCameraCalibrationSignature.Get ()); + + } + + // Embedded camera profiles. + + if (shared.fCameraProfile.fColorPlanes > 1) + { + + if (qDNGValidate || host.NeedsMeta () || host.NeedsImage ()) + { + + // Add profile from main IFD. + + { + + AutoPtr profile (new dng_camera_profile ()); + + dng_camera_profile_info &profileInfo = shared.fCameraProfile; + + profile->Parse (stream, profileInfo); + + // The main embedded profile must be valid. + + if (!profile->IsValid (shared.fCameraProfile.fColorPlanes)) + { + + ThrowBadFormat (); + + } + + profile->SetWasReadFromDNG (); + + AddProfile (profile); + + } + + // Extra profiles. + + for (uint32 index = 0; index < (uint32) shared.fExtraCameraProfiles.size (); index++) + { + + try + { + + AutoPtr profile (new dng_camera_profile ()); + + dng_camera_profile_info &profileInfo = shared.fExtraCameraProfiles [index]; + + profile->Parse (stream, profileInfo); + + if (!profile->IsValid (shared.fCameraProfile.fColorPlanes)) + { + + ThrowBadFormat (); + + } + + profile->SetWasReadFromDNG (); + + AddProfile (profile); + + } + + catch (dng_exception &except) + { + + // Don't ignore transient errors. + + if (host.IsTransientError (except.ErrorCode ())) + { + + throw; + + } + + // Eat other parsing errors. + + #if qDNGValidate + + ReportWarning ("Unable to parse extra profile"); + + #endif + + } + + } + + } + + // As shot profile name. + + if (shared.fAsShotProfileName.NotEmpty ()) + { + + SetAsShotProfileName (shared.fAsShotProfileName.Get ()); + + } + + } + + // Raw image data digest. + + if (shared.fRawImageDigest.IsValid ()) + { + + SetRawImageDigest (shared.fRawImageDigest); + + } + + // New raw image data digest. + + if (shared.fNewRawImageDigest.IsValid ()) + { + + SetNewRawImageDigest (shared.fNewRawImageDigest); + + } + + // Raw data unique ID. + + if (shared.fRawDataUniqueID.IsValid ()) + { + + SetRawDataUniqueID (shared.fRawDataUniqueID); + + } + + // Original raw file name. + + if (shared.fOriginalRawFileName.NotEmpty ()) + { + + SetOriginalRawFileName (shared.fOriginalRawFileName.Get ()); + + } + + // Original raw file data. + + if (shared.fOriginalRawFileDataCount) + { + + SetHasOriginalRawFileData (true); + + if (host.KeepOriginalFile ()) + { + + uint32 count = shared.fOriginalRawFileDataCount; + + AutoPtr block (host.Allocate (count)); + + stream.SetReadPosition (shared.fOriginalRawFileDataOffset); + + stream.Get (block->Buffer (), count); + + SetOriginalRawFileData (block); + + SetOriginalRawFileDigest (shared.fOriginalRawFileDigest); + + ValidateOriginalRawFileDigest (); + + } + + } + + // DNG private data. + + if (shared.fDNGPrivateDataCount && (host.SaveDNGVersion () != dngVersion_None)) + { + + uint32 length = shared.fDNGPrivateDataCount; + + AutoPtr block (host.Allocate (length)); + + stream.SetReadPosition (shared.fDNGPrivateDataOffset); + + stream.Get (block->Buffer (), length); + + SetPrivateData (block); + + } + + // Hand off EXIF metadata to negative. + + ResetExif (info.fExif.Release ()); + + // Parse linearization info. + + NeedLinearizationInfo (); + + fLinearizationInfo.Get ()->Parse (host, + stream, + info); + + // Parse mosaic info. + + if (rawIFD.fPhotometricInterpretation == piCFA) + { + + NeedMosaicInfo (); + + fMosaicInfo.Get ()->Parse (host, + stream, + info); + + } + + // Fill in original sizes. + + if (shared.fOriginalDefaultFinalSize.h > 0 && + shared.fOriginalDefaultFinalSize.v > 0) + { + + SetOriginalDefaultFinalSize (shared.fOriginalDefaultFinalSize); + + SetOriginalBestQualityFinalSize (shared.fOriginalDefaultFinalSize); + + SetOriginalDefaultCropSize (dng_urational (shared.fOriginalDefaultFinalSize.h, 1), + dng_urational (shared.fOriginalDefaultFinalSize.v, 1)); + + } + + if (shared.fOriginalBestQualityFinalSize.h > 0 && + shared.fOriginalBestQualityFinalSize.v > 0) + { + + SetOriginalBestQualityFinalSize (shared.fOriginalBestQualityFinalSize); + + } + + if (shared.fOriginalDefaultCropSizeH.As_real64 () >= 1.0 && + shared.fOriginalDefaultCropSizeV.As_real64 () >= 1.0) + { + + SetOriginalDefaultCropSize (shared.fOriginalDefaultCropSizeH, + shared.fOriginalDefaultCropSizeV); + + } + + } + +/*****************************************************************************/ + +void dng_negative::SetDefaultOriginalSizes () + { + + // Fill in original sizes if we don't have them already. + + if (OriginalDefaultFinalSize () == dng_point ()) + { + + SetOriginalDefaultFinalSize (dng_point (DefaultFinalHeight (), + DefaultFinalWidth ())); + + } + + if (OriginalBestQualityFinalSize () == dng_point ()) + { + + SetOriginalBestQualityFinalSize (dng_point (BestQualityFinalHeight (), + BestQualityFinalWidth ())); + + } + + if (OriginalDefaultCropSizeH ().NotValid () || + OriginalDefaultCropSizeV ().NotValid ()) + { + + SetOriginalDefaultCropSize (DefaultCropSizeH (), + DefaultCropSizeV ()); + + } + + } + +/*****************************************************************************/ + +void dng_negative::PostParse (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + // Shared info. + + dng_shared &shared = *(info.fShared.Get ()); + + if (host.NeedsMeta ()) + { + + // Fill in original sizes if we don't have them already. + + SetDefaultOriginalSizes (); + + // MakerNote. + + if (shared.fMakerNoteCount) + { + + // See if we know if the MakerNote is safe or not. + + SetMakerNoteSafety (shared.fMakerNoteSafety == 1); + + // If the MakerNote is safe, preserve it as a MakerNote. + + if (IsMakerNoteSafe ()) + { + + AutoPtr block (host.Allocate (shared.fMakerNoteCount)); + + stream.SetReadPosition (shared.fMakerNoteOffset); + + stream.Get (block->Buffer (), shared.fMakerNoteCount); + + SetMakerNote (block); + + } + + } + + // IPTC metadata. + + if (shared.fIPTC_NAA_Count) + { + + AutoPtr block (host.Allocate (shared.fIPTC_NAA_Count)); + + stream.SetReadPosition (shared.fIPTC_NAA_Offset); + + uint64 iptcOffset = stream.PositionInOriginalFile(); + + stream.Get (block->Buffer (), + block->LogicalSize ()); + + SetIPTC (block, iptcOffset); + + } + + // XMP metadata. + + if (shared.fXMPCount) + { + + AutoPtr block (host.Allocate (shared.fXMPCount)); + + stream.SetReadPosition (shared.fXMPOffset); + + stream.Get (block->Buffer (), + block->LogicalSize ()); + + Metadata ().SetEmbeddedXMP (host, + block->Buffer (), + block->LogicalSize ()); + + #if qDNGValidate + + if (!Metadata ().HaveValidEmbeddedXMP ()) + { + ReportError ("The embedded XMP is invalid"); + } + + #endif + + } + + // Color info. + + if (!IsMonochrome ()) + { + + // If the ColorimetricReference is the ICC profile PCS, + // then the data must be already be white balanced to the + // ICC profile PCS white point. + + if (ColorimetricReference () == crICCProfilePCS) + { + + ClearCameraNeutral (); + + SetCameraWhiteXY (PCStoXY ()); + + } + + else + { + + // AsShotNeutral. + + if (shared.fAsShotNeutral.Count () == ColorChannels ()) + { + + SetCameraNeutral (shared.fAsShotNeutral); + + } + + // AsShotWhiteXY. + + if (shared.fAsShotWhiteXY.IsValid () && !HasCameraNeutral ()) + { + + SetCameraWhiteXY (shared.fAsShotWhiteXY); + + } + + } + + } + + } + + } + +/*****************************************************************************/ + +bool dng_negative::SetFourColorBayer () + { + + if (ColorChannels () != 3) + { + return false; + } + + if (!fMosaicInfo.Get ()) + { + return false; + } + + if (!fMosaicInfo.Get ()->SetFourColorBayer ()) + { + return false; + } + + SetColorChannels (4); + + if (fCameraNeutral.Count () == 3) + { + + dng_vector n (4); + + n [0] = fCameraNeutral [0]; + n [1] = fCameraNeutral [1]; + n [2] = fCameraNeutral [2]; + n [3] = fCameraNeutral [1]; + + fCameraNeutral = n; + + } + + fCameraCalibration1.Clear (); + fCameraCalibration2.Clear (); + + fCameraCalibrationSignature.Clear (); + + for (uint32 index = 0; index < (uint32) fCameraProfile.size (); index++) + { + + fCameraProfile [index]->SetFourColorBayer (); + + } + + return true; + + } + +/*****************************************************************************/ + +const dng_image & dng_negative::RawImage () const + { + + if (fRawImage.Get ()) + { + return *fRawImage.Get (); + } + + if (fStage1Image.Get ()) + { + return *fStage1Image.Get (); + } + + if (fUnflattenedStage3Image.Get ()) + { + return *fUnflattenedStage3Image.Get (); + } + + DNG_ASSERT (fStage3Image.Get (), + "dng_negative::RawImage with no raw image"); + + return *fStage3Image.Get (); + + } + +/*****************************************************************************/ + +const dng_jpeg_image * dng_negative::RawJPEGImage () const + { + + return fRawJPEGImage.Get (); + + } + +/*****************************************************************************/ + +void dng_negative::SetRawJPEGImage (AutoPtr &jpegImage) + { + + fRawJPEGImage.Reset (jpegImage.Release ()); + + } + +/*****************************************************************************/ + +void dng_negative::ClearRawJPEGImage () + { + + fRawJPEGImage.Reset (); + + } + +/*****************************************************************************/ + +void dng_negative::FindRawJPEGImageDigest (dng_host &host) const + { + + if (fRawJPEGImageDigest.IsNull ()) + { + + if (fRawJPEGImage.Get ()) + { + + #if qDNGValidate + + dng_timer timer ("FindRawJPEGImageDigest time"); + + #endif + + fRawJPEGImageDigest = fRawJPEGImage->FindDigest (host); + + } + + else + { + + ThrowProgramError ("No raw JPEG image"); + + } + + } + + } + +#if GPR_READING + +bool dng_negative::IsVc5Image(dng_info &info) +{ + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex].Get (); + + return rawIFD.fCompression == ccVc5; +} + +void dng_negative::ReadVc5Image (dng_host &host, + dng_stream &stream, + dng_info &info, + dng_read_image &imageReader) +{ + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex].Get (); + + + fRawImage.Reset (host.Make_dng_image (rawIFD.Bounds (), + rawIFD.fSamplesPerPixel, + rawIFD.PixelType ())); + imageReader.Read (host, + rawIFD, + stream, + *fRawImage.Get (), + NULL, + NULL); +} +#endif + +/*****************************************************************************/ + +void dng_negative::ReadStage1Image (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + // Allocate image we are reading. + + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex].Get (); + + fStage1Image.Reset (host.Make_dng_image (rawIFD.Bounds (), + rawIFD.fSamplesPerPixel, + rawIFD.PixelType ())); + + // See if we should grab the compressed JPEG data. + + AutoPtr jpegImage; + + if (host.SaveDNGVersion () >= dngVersion_1_4_0_0 && + !host.PreferredSize () && + !host.ForPreview () && + rawIFD.fCompression == ccLossyJPEG) + { + + jpegImage.Reset (new dng_jpeg_image); + + } + + // See if we need to compute the digest of the compressed JPEG data + // while reading. + + bool needJPEGDigest = (RawImageDigest ().IsValid () || + NewRawImageDigest ().IsValid ()) && + rawIFD.fCompression == ccLossyJPEG && + jpegImage.Get () == NULL; + + dng_fingerprint jpegDigest; + + // Read the image. + + rawIFD.ReadImage (host, + stream, + *fStage1Image.Get (), + jpegImage.Get (), + needJPEGDigest ? &jpegDigest : NULL); + + // Remember the raw floating point bit depth, if reading from + // a floating point image. + + if (fStage1Image->PixelType () == ttFloat) + { + + SetRawFloatBitDepth (rawIFD.fBitsPerSample [0]); + + } + + // Remember the compressed JPEG data if we read it. + + if (jpegImage.Get ()) + { + + SetRawJPEGImage (jpegImage); + + } + + // Remember the compressed JPEG digest if we computed it. + + if (jpegDigest.IsValid ()) + { + + SetRawJPEGImageDigest (jpegDigest); + + } + + // We are are reading the main image, we should read the opcode lists + // also. + + if (rawIFD.fOpcodeList1Count) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("\nParsing OpcodeList1: "); + } + + #endif + + fOpcodeList1.Parse (host, + stream, + rawIFD.fOpcodeList1Count, + rawIFD.fOpcodeList1Offset); + + } + + if (rawIFD.fOpcodeList2Count) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("\nParsing OpcodeList2: "); + } + + #endif + + fOpcodeList2.Parse (host, + stream, + rawIFD.fOpcodeList2Count, + rawIFD.fOpcodeList2Offset); + + } + + if (rawIFD.fOpcodeList3Count) + { + + #if qDNGValidate + + if (gVerbose) + { + printf ("\nParsing OpcodeList3: "); + } + + #endif + + fOpcodeList3.Parse (host, + stream, + rawIFD.fOpcodeList3Count, + rawIFD.fOpcodeList3Offset); + + } + + } + +/*****************************************************************************/ + +void dng_negative::SetStage1Image (AutoPtr &image) + { + + fStage1Image.Reset (image.Release ()); + + } + +/*****************************************************************************/ + +void dng_negative::SetStage2Image (AutoPtr &image) + { + + fStage2Image.Reset (image.Release ()); + + } + +/*****************************************************************************/ + +void dng_negative::SetStage3Image (AutoPtr &image) + { + + fStage3Image.Reset (image.Release ()); + + } + +/*****************************************************************************/ + +void dng_negative::DoBuildStage2 (dng_host &host) + { + + dng_image &stage1 = *fStage1Image.Get (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + uint32 pixelType = ttShort; + + if (stage1.PixelType () == ttLong || + stage1.PixelType () == ttFloat) + { + + pixelType = ttFloat; + + } + + fStage2Image.Reset (host.Make_dng_image (info.fActiveArea.Size (), + stage1.Planes (), + pixelType)); + + info.Linearize (host, + stage1, + *fStage2Image.Get ()); + + } + +/*****************************************************************************/ + +void dng_negative::DoPostOpcodeList2 (dng_host & /* host */) + { + + // Nothing by default. + + } + +/*****************************************************************************/ + +bool dng_negative::NeedDefloatStage2 (dng_host &host) + { + + if (fStage2Image->PixelType () == ttFloat) + { + + if (fRawImageStage >= rawImageStagePostOpcode2 && + host.SaveDNGVersion () != dngVersion_None && + host.SaveDNGVersion () < dngVersion_1_4_0_0) + { + + return true; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_negative::DefloatStage2 (dng_host & /* host */) + { + + ThrowNotYetImplemented ("dng_negative::DefloatStage2"); + + } + +/*****************************************************************************/ + +void dng_negative::BuildStage2Image (dng_host &host) + { + + // If reading the negative to save in DNG format, figure out + // when to grab a copy of the raw data. + + if (host.SaveDNGVersion () != dngVersion_None) + { + + // Transparency masks are only supported in DNG version 1.4 and + // later. In this case, the flattening of the transparency mask happens + // on the the stage3 image. + + if (TransparencyMask () && host.SaveDNGVersion () < dngVersion_1_4_0_0) + { + fRawImageStage = rawImageStagePostOpcode3; + } + + else if (fOpcodeList3.MinVersion (false) > host.SaveDNGVersion () || + fOpcodeList3.AlwaysApply ()) + { + fRawImageStage = rawImageStagePostOpcode3; + } + + else if (host.SaveLinearDNG (*this)) + { + + // If the opcode list 3 has optional tags that are beyond the + // the minimum version, and we are saving a linear DNG anyway, + // then go ahead and apply them. + + if (fOpcodeList3.MinVersion (true) > host.SaveDNGVersion ()) + { + fRawImageStage = rawImageStagePostOpcode3; + } + + else + { + fRawImageStage = rawImageStagePreOpcode3; + } + + } + + else if (fOpcodeList2.MinVersion (false) > host.SaveDNGVersion () || + fOpcodeList2.AlwaysApply ()) + { + fRawImageStage = rawImageStagePostOpcode2; + } + + else if (fOpcodeList1.MinVersion (false) > host.SaveDNGVersion () || + fOpcodeList1.AlwaysApply ()) + { + fRawImageStage = rawImageStagePostOpcode1; + } + + else + { + fRawImageStage = rawImageStagePreOpcode1; + } + + // We should not save floating point stage1 images unless the target + // DNG version is high enough to understand floating point images. + // We handle this by converting from floating point to integer if + // required after building stage2 image. + + if (fStage1Image->PixelType () == ttFloat) + { + + if (fRawImageStage < rawImageStagePostOpcode2) + { + + if (host.SaveDNGVersion () < dngVersion_1_4_0_0) + { + + fRawImageStage = rawImageStagePostOpcode2; + + } + + } + + } + + } + + // Grab clone of raw image if required. + + if (fRawImageStage == rawImageStagePreOpcode1) + { + + fRawImage.Reset (fStage1Image->Clone ()); + + if (fTransparencyMask.Get ()) + { + fRawTransparencyMask.Reset (fTransparencyMask->Clone ()); + } + + } + + else + { + + // If we are not keeping the most raw image, we need + // to recompute the raw image digest. + + ClearRawImageDigest (); + + // If we don't grab the unprocessed stage 1 image, then + // the raw JPEG image is no longer valid. + + ClearRawJPEGImage (); + + // Nor is the digest of the raw JPEG data. + + ClearRawJPEGImageDigest (); + + // We also don't know the raw floating point bit depth. + + SetRawFloatBitDepth (0); + + } + + // Process opcode list 1. + + host.ApplyOpcodeList (fOpcodeList1, *this, fStage1Image); + + // See if we are done with the opcode list 1. + + if (fRawImageStage > rawImageStagePreOpcode1) + { + + fOpcodeList1.Clear (); + + } + + // Grab clone of raw image if required. + + if (fRawImageStage == rawImageStagePostOpcode1) + { + + fRawImage.Reset (fStage1Image->Clone ()); + + if (fTransparencyMask.Get ()) + { + fRawTransparencyMask.Reset (fTransparencyMask->Clone ()); + } + + } + + // Finalize linearization info. + + { + + NeedLinearizationInfo (); + + dng_linearization_info &info = *fLinearizationInfo.Get (); + + info.PostParse (host, *this); + + } + + // Perform the linearization. + + DoBuildStage2 (host); + + // Delete the stage1 image now that we have computed the stage 2 image. + + fStage1Image.Reset (); + + // Are we done with the linearization info. + + if (fRawImageStage > rawImageStagePostOpcode1) + { + + ClearLinearizationInfo (); + + } + + // Process opcode list 2. + + host.ApplyOpcodeList (fOpcodeList2, *this, fStage2Image); + + // See if we are done with the opcode list 2. + + if (fRawImageStage > rawImageStagePostOpcode1) + { + + fOpcodeList2.Clear (); + + } + + // Hook for any required processing just after opcode list 2. + + DoPostOpcodeList2 (host); + + // Convert from floating point to integer if required. + + if (NeedDefloatStage2 (host)) + { + + DefloatStage2 (host); + + } + + // Grab clone of raw image if required. + + if (fRawImageStage == rawImageStagePostOpcode2) + { + + fRawImage.Reset (fStage2Image->Clone ()); + + if (fTransparencyMask.Get ()) + { + fRawTransparencyMask.Reset (fTransparencyMask->Clone ()); + } + + } + + } + +/*****************************************************************************/ + +void dng_negative::DoInterpolateStage3 (dng_host &host, + int32 srcPlane) + { + + dng_image &stage2 = *fStage2Image.Get (); + + dng_mosaic_info &info = *fMosaicInfo.Get (); + + dng_point downScale = info.DownScale (host.MinimumSize (), + host.PreferredSize (), + host.CropFactor ()); + + if (downScale != dng_point (1, 1)) + { + SetIsPreview (true); + } + + dng_point dstSize = info.DstSize (downScale); + + fStage3Image.Reset (host.Make_dng_image (dng_rect (dstSize), + info.fColorPlanes, + stage2.PixelType ())); + + if (srcPlane < 0 || srcPlane >= (int32) stage2.Planes ()) + { + srcPlane = 0; + } + + info.Interpolate (host, + *this, + stage2, + *fStage3Image.Get (), + downScale, + srcPlane); + + } + +/*****************************************************************************/ + +// Interpolate and merge a multi-channel CFA image. + +void dng_negative::DoMergeStage3 (dng_host &host) + { + + // The DNG SDK does not provide multi-channel CFA image merging code. + // It just grabs the first channel and uses that. + + DoInterpolateStage3 (host, 0); + + // Just grabbing the first channel would often result in the very + // bright image using the baseline exposure value. + + fStage3Gain = pow (2.0, BaselineExposure ()); + + } + +/*****************************************************************************/ + +void dng_negative::DoBuildStage3 (dng_host &host, + int32 srcPlane) + { + + // If we don't have a mosaic pattern, then just move the stage 2 + // image on to stage 3. + + dng_mosaic_info *info = fMosaicInfo.Get (); + + if (!info || !info->IsColorFilterArray ()) + { + + fStage3Image.Reset (fStage2Image.Release ()); + + } + + else + { + + // Remember the size of the stage 2 image. + + dng_point stage2_size = fStage2Image->Size (); + + // Special case multi-channel CFA interpolation. + + if ((fStage2Image->Planes () > 1) && (srcPlane < 0)) + { + + DoMergeStage3 (host); + + } + + // Else do a single channel interpolation. + + else + { + + DoInterpolateStage3 (host, srcPlane); + + } + + // Calculate the ratio of the stage 3 image size to stage 2 image size. + + dng_point stage3_size = fStage3Image->Size (); + + fRawToFullScaleH = (real64) stage3_size.h / (real64) stage2_size.h; + fRawToFullScaleV = (real64) stage3_size.v / (real64) stage2_size.v; + + } + + } + +/*****************************************************************************/ + +void dng_negative::BuildStage3Image (dng_host &host, + int32 srcPlane) + { + + // Finalize the mosaic information. + + dng_mosaic_info *info = fMosaicInfo.Get (); + + if (info) + { + + info->PostParse (host, *this); + + } + + // Do the interpolation as required. + + DoBuildStage3 (host, srcPlane); + + // Delete the stage2 image now that we have computed the stage 3 image. + + fStage2Image.Reset (); + + // Are we done with the mosaic info? + + if (fRawImageStage >= rawImageStagePreOpcode3) + { + + ClearMosaicInfo (); + + // To support saving linear DNG files, to need to account for + // and upscaling during interpolation. + + if (fRawToFullScaleH > 1.0) + { + + uint32 adjust = Round_uint32 (fRawToFullScaleH); + + fDefaultCropSizeH .n *= adjust; + fDefaultCropOriginH.n *= adjust; + fDefaultScaleH .d *= adjust; + + fRawToFullScaleH /= (real64) adjust; + + } + + if (fRawToFullScaleV > 1.0) + { + + uint32 adjust = Round_uint32 (fRawToFullScaleV); + + fDefaultCropSizeV .n *= adjust; + fDefaultCropOriginV.n *= adjust; + fDefaultScaleV .d *= adjust; + + fRawToFullScaleV /= (real64) adjust; + + } + + } + + // Resample the transparency mask if required. + + ResizeTransparencyToMatchStage3 (host); + + // Grab clone of raw image if required. + + if (fRawImageStage == rawImageStagePreOpcode3) + { + + fRawImage.Reset (fStage3Image->Clone ()); + + if (fTransparencyMask.Get ()) + { + fRawTransparencyMask.Reset (fTransparencyMask->Clone ()); + } + + } + + // Process opcode list 3. + + host.ApplyOpcodeList (fOpcodeList3, *this, fStage3Image); + + // See if we are done with the opcode list 3. + + if (fRawImageStage > rawImageStagePreOpcode3) + { + + fOpcodeList3.Clear (); + + } + + // Just in case the opcode list 3 changed the image size, resample the + // transparency mask again if required. This is nearly always going + // to be a fast NOP operation. + + ResizeTransparencyToMatchStage3 (host); + + // Don't need to grab a copy of raw data at this stage since + // it is kept around as the stage 3 image. + + } + +/******************************************************************************/ + +class dng_gamma_encode_proxy : public dng_1d_function + { + + private: + + real64 fBlack; + real64 fWhite; + + bool fIsSceneReferred; + + real64 scale; + real64 t1; + + public: + + dng_gamma_encode_proxy (real64 black, + real64 white, + bool isSceneReferred) + + : fBlack (black) + , fWhite (white) + , fIsSceneReferred (isSceneReferred) + + , scale (1.0 / (fWhite - fBlack)) + , t1 (1.0 / (27.0 * pow (5.0, 3.0 / 2.0))) + + { + } + + virtual real64 Evaluate (real64 x) const + { + + x = Pin_real64 (0.0, (x - fBlack) * scale, 1.0); + + real64 y; + + if (fIsSceneReferred) + { + + real64 t = pow (sqrt (25920.0 * x * x + 1.0) * t1 + x * (8.0 / 15.0), 1.0 / 3.0); + + y = t - 1.0 / (45.0 * t); + + DNG_ASSERT (Abs_real64 (x - (y / 16.0 + y * y * y * 15.0 / 16.0)) < 0.0000001, + "Round trip error"); + + } + + else + { + + y = (sqrt (960.0 * x + 1.0) - 1.0) / 30.0; + + DNG_ASSERT (Abs_real64 (x - (y / 16.0 + y * y * (15.0 / 16.0))) < 0.0000001, + "Round trip error"); + + } + + return y; + + } + + }; + +/*****************************************************************************/ + +class dng_encode_proxy_task: public dng_area_task + { + + private: + + const dng_image &fSrcImage; + + dng_image &fDstImage; + + AutoPtr fTable16 [kMaxColorPlanes]; + + public: + + dng_encode_proxy_task (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + const real64 *black, + const real64 *white, + bool isSceneReferred); + + virtual dng_rect RepeatingTile1 () const + { + return fSrcImage.RepeatingTile (); + } + + virtual dng_rect RepeatingTile2 () const + { + return fDstImage.RepeatingTile (); + } + + virtual void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer *sniffer); + + private: + + // Hidden copy constructor and assignment operator. + + dng_encode_proxy_task (const dng_encode_proxy_task &task); + + dng_encode_proxy_task & operator= (const dng_encode_proxy_task &task); + + }; + +/*****************************************************************************/ + +dng_encode_proxy_task::dng_encode_proxy_task (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + const real64 *black, + const real64 *white, + bool isSceneReferred) + + : fSrcImage (srcImage) + , fDstImage (dstImage) + + { + + for (uint32 plane = 0; plane < fSrcImage.Planes (); plane++) + { + + dng_gamma_encode_proxy gamma (black [plane], + white [plane], + isSceneReferred); + + dng_1d_table table32; + + table32.Initialize (host.Allocator (), gamma); + + fTable16 [plane] . Reset (host.Allocate (0x10000 * sizeof (uint16))); + + table32.Expand16 (fTable16 [plane]->Buffer_uint16 ()); + + } + + } + +/*****************************************************************************/ + +void dng_encode_proxy_task::Process (uint32 /* threadIndex */, + const dng_rect &tile, + dng_abort_sniffer * /* sniffer */) + { + + dng_const_tile_buffer srcBuffer (fSrcImage, tile); + dng_dirty_tile_buffer dstBuffer (fDstImage, tile); + + int32 sColStep = srcBuffer.fColStep; + int32 dColStep = dstBuffer.fColStep; + + const uint16 *noise = dng_dither::Get ().NoiseBuffer16 (); + + for (uint32 plane = 0; plane < fSrcImage.Planes (); plane++) + { + + const uint16 *map = fTable16 [plane]->Buffer_uint16 (); + + for (int32 row = tile.t; row < tile.b; row++) + { + + const uint16 *sPtr = srcBuffer.ConstPixel_uint16 (row, tile.l, plane); + + uint8 *dPtr = dstBuffer.DirtyPixel_uint8 (row, tile.l, plane); + + const uint16 *rPtr = &noise [(row & dng_dither::kRNGMask) * dng_dither::kRNGSize]; + + for (int32 col = tile.l; col < tile.r; col++) + { + + uint32 x = *sPtr; + + uint32 r = rPtr [col & dng_dither::kRNGMask]; + + x = map [x]; + + x = (((x << 8) - x) + r) >> 16; + + *dPtr = (uint8) x; + + sPtr += sColStep; + dPtr += dColStep; + + } + + } + + } + + } + +/******************************************************************************/ + +dng_image * dng_negative::EncodeRawProxy (dng_host &host, + const dng_image &srcImage, + dng_opcode_list &opcodeList) const + { + + if (srcImage.PixelType () != ttShort) + { + return NULL; + } + + real64 black [kMaxColorPlanes]; + real64 white [kMaxColorPlanes]; + + bool isSceneReferred = (ColorimetricReference () == crSceneReferred); + + { + + const real64 kClipFraction = 0.00001; + + uint64 pixels = (uint64) srcImage.Bounds ().H () * + (uint64) srcImage.Bounds ().W (); + + uint32 limit = Round_int32 ((real64) pixels * kClipFraction); + + AutoPtr histData (host.Allocate (65536 * sizeof (uint32))); + + uint32 *hist = histData->Buffer_uint32 (); + + for (uint32 plane = 0; plane < srcImage.Planes (); plane++) + { + + HistogramArea (host, + srcImage, + srcImage.Bounds (), + hist, + 65535, + plane); + + uint32 total = 0; + + uint32 upper = 65535; + + while (total + hist [upper] <= limit && upper > 255) + { + + total += hist [upper]; + + upper--; + + } + + total = 0; + + uint32 lower = 0; + + while (total + hist [lower] <= limit && lower < upper - 255) + { + + total += hist [lower]; + + lower++; + + } + + black [plane] = lower / 65535.0; + white [plane] = upper / 65535.0; + + } + + } + + // Apply the gamma encoding, using dither when downsampling to 8-bit. + + AutoPtr dstImage (host.Make_dng_image (srcImage.Bounds (), + srcImage.Planes (), + ttByte)); + + { + + dng_encode_proxy_task task (host, + srcImage, + *dstImage, + black, + white, + isSceneReferred); + + host.PerformAreaTask (task, + srcImage.Bounds ()); + + } + + // Add opcodes to undo the gamma encoding. + + { + + for (uint32 plane = 0; plane < srcImage.Planes (); plane++) + { + + dng_area_spec areaSpec (srcImage.Bounds (), + plane); + + real64 coefficient [4]; + + coefficient [0] = 0.0; + coefficient [1] = 1.0 / 16.0; + + if (isSceneReferred) + { + coefficient [2] = 0.0; + coefficient [3] = 15.0 / 16.0; + } + else + { + coefficient [2] = 15.0 / 16.0; + coefficient [3] = 0.0; + } + + coefficient [0] *= white [plane] - black [plane]; + coefficient [1] *= white [plane] - black [plane]; + coefficient [2] *= white [plane] - black [plane]; + coefficient [3] *= white [plane] - black [plane]; + + coefficient [0] += black [plane]; + + AutoPtr opcode (new dng_opcode_MapPolynomial (areaSpec, + isSceneReferred ? 3 : 2, + coefficient)); + + opcodeList.Append (opcode); + + } + + } + + return dstImage.Release (); + + } + +/******************************************************************************/ + +void dng_negative::AdjustProfileForStage3 () + { + + // For dng_sdk, the stage3 image's color space is always the same as the + // raw image's color space. + + } + +/******************************************************************************/ + +void dng_negative::ConvertToProxy (dng_host &host, + dng_image_writer &writer, + uint32 proxySize, + uint64 proxyCount) + { + + if (!proxySize) + { + proxySize = kMaxImageSide; + } + + if (!proxyCount) + { + proxyCount = (uint64) proxySize * proxySize; + } + + // Don't need to private data around in non-full size proxies. + + if (proxySize < kMaxImageSide || + proxyCount < kMaxImageSide * kMaxImageSide) + { + + ClearMakerNote (); + + ClearPrivateData (); + + } + + // See if we already have an acceptable proxy image. + + if (fRawImage.Get () && + fRawImage->PixelType () == ttByte && + fRawImage->Bounds () == DefaultCropArea () && + fRawImage->Bounds ().H () <= proxySize && + fRawImage->Bounds ().W () <= proxySize && + (uint64) fRawImage->Bounds ().H () * + (uint64) fRawImage->Bounds ().W () <= proxyCount && + (!GetMosaicInfo () || !GetMosaicInfo ()->IsColorFilterArray ()) && + fRawJPEGImage.Get () && + (!RawTransparencyMask () || RawTransparencyMask ()->PixelType () == ttByte)) + { + + return; + + } + + if (fRawImage.Get () && + fRawImage->PixelType () == ttFloat && + fRawImage->Bounds ().H () <= proxySize && + fRawImage->Bounds ().W () <= proxySize && + (uint64) fRawImage->Bounds ().H () * + (uint64) fRawImage->Bounds ().W () <= proxyCount && + RawFloatBitDepth () == 16 && + (!RawTransparencyMask () || RawTransparencyMask ()->PixelType () == ttByte)) + { + + return; + + } + + // Clear any grabbed raw image, since we are going to start + // building the proxy with the stage3 image. + + fRawImage.Reset (); + + ClearRawJPEGImage (); + + SetRawFloatBitDepth (0); + + ClearLinearizationInfo (); + + ClearMosaicInfo (); + + fOpcodeList1.Clear (); + fOpcodeList2.Clear (); + fOpcodeList3.Clear (); + + // Adjust the profile to match the stage 3 image, if required. + + AdjustProfileForStage3 (); + + // Not saving the raw-most image, do the old raw digest is no + // longer valid. + + ClearRawImageDigest (); + + ClearRawJPEGImageDigest (); + + // Trim off extra pixels outside the default crop area. + + dng_rect defaultCropArea = DefaultCropArea (); + + if (Stage3Image ()->Bounds () != defaultCropArea) + { + + fStage3Image->Trim (defaultCropArea); + + if (fTransparencyMask.Get ()) + { + fTransparencyMask->Trim (defaultCropArea); + } + + fDefaultCropOriginH = dng_urational (0, 1); + fDefaultCropOriginV = dng_urational (0, 1); + + } + + // Figure out the requested proxy pixel size. + + real64 aspectRatio = AspectRatio (); + + dng_point newSize (proxySize, proxySize); + + if (aspectRatio >= 1.0) + { + newSize.v = Max_int32 (1, Round_int32 (proxySize / aspectRatio)); + } + else + { + newSize.h = Max_int32 (1, Round_int32 (proxySize * aspectRatio)); + } + + newSize.v = Min_int32 (newSize.v, DefaultFinalHeight ()); + newSize.h = Min_int32 (newSize.h, DefaultFinalWidth ()); + + if ((uint64) newSize.v * + (uint64) newSize.h > proxyCount) + { + + if (aspectRatio >= 1.0) + { + + newSize.h = (uint32) sqrt (proxyCount * aspectRatio); + + newSize.v = Max_int32 (1, Round_int32 (newSize.h / aspectRatio)); + + } + + else + { + + newSize.v = (uint32) sqrt (proxyCount / aspectRatio); + + newSize.h = Max_int32 (1, Round_int32 (newSize.v * aspectRatio)); + + } + + } + + // If this is fewer pixels, downsample the stage 3 image to that size. + + dng_point oldSize = defaultCropArea.Size (); + + if ((uint64) newSize.v * (uint64) newSize.h < + (uint64) oldSize.v * (uint64) oldSize.h) + { + + const dng_image &srcImage (*Stage3Image ()); + + AutoPtr dstImage (host.Make_dng_image (newSize, + srcImage.Planes (), + srcImage.PixelType ())); + + host.ResampleImage (srcImage, + *dstImage); + + fStage3Image.Reset (dstImage.Release ()); + + fDefaultCropSizeH = dng_urational (newSize.h, 1); + fDefaultCropSizeV = dng_urational (newSize.v, 1); + + fDefaultScaleH = dng_urational (1, 1); + fDefaultScaleV = dng_urational (1, 1); + + fBestQualityScale = dng_urational (1, 1); + + fRawToFullScaleH = 1.0; + fRawToFullScaleV = 1.0; + + } + + // Convert 32-bit floating point images to 16-bit floating point to + // save space. + + if (Stage3Image ()->PixelType () == ttFloat) + { + + fRawImage.Reset (host.Make_dng_image (Stage3Image ()->Bounds (), + Stage3Image ()->Planes (), + ttFloat)); + + LimitFloatBitDepth (host, + *Stage3Image (), + *fRawImage, + 16, + 32768.0f); + + SetRawFloatBitDepth (16); + + SetWhiteLevel (32768); + + } + + else + { + + // Convert 16-bit deep images to 8-bit deep image for saving. + + fRawImage.Reset (EncodeRawProxy (host, + *Stage3Image (), + fOpcodeList2)); + + if (fRawImage.Get ()) + { + + SetWhiteLevel (255); + + // Compute JPEG compressed version. + + if (fRawImage->PixelType () == ttByte && + host.SaveDNGVersion () >= dngVersion_1_4_0_0) + { + + AutoPtr jpegImage (new dng_jpeg_image); + + jpegImage->Encode (host, + *this, + writer, + *fRawImage); + + SetRawJPEGImage (jpegImage); + + } + + } + + } + + // Deal with transparency mask. + + if (TransparencyMask ()) + { + + const bool convertTo8Bit = true; + + ResizeTransparencyToMatchStage3 (host, convertTo8Bit); + + fRawTransparencyMask.Reset (fTransparencyMask->Clone ()); + + } + + // Recompute the raw data unique ID, since we changed the image data. + + RecomputeRawDataUniqueID (host); + + } + +/*****************************************************************************/ + +dng_linearization_info * dng_negative::MakeLinearizationInfo () + { + + dng_linearization_info *info = new dng_linearization_info (); + + if (!info) + { + ThrowMemoryFull (); + } + + return info; + + } + +/*****************************************************************************/ + +void dng_negative::NeedLinearizationInfo () + { + + if (!fLinearizationInfo.Get ()) + { + + fLinearizationInfo.Reset (MakeLinearizationInfo ()); + + } + + } + +/*****************************************************************************/ + +dng_mosaic_info * dng_negative::MakeMosaicInfo () + { + + dng_mosaic_info *info = new dng_mosaic_info (); + + if (!info) + { + ThrowMemoryFull (); + } + + return info; + + } + +/*****************************************************************************/ + +void dng_negative::NeedMosaicInfo () + { + + if (!fMosaicInfo.Get ()) + { + + fMosaicInfo.Reset (MakeMosaicInfo ()); + + } + + } + +/*****************************************************************************/ + +void dng_negative::SetTransparencyMask (AutoPtr &image, + uint32 bitDepth) + { + + fTransparencyMask.Reset (image.Release ()); + + fRawTransparencyMaskBitDepth = bitDepth; + + } + +/*****************************************************************************/ + +const dng_image * dng_negative::TransparencyMask () const + { + + return fTransparencyMask.Get (); + + } + +/*****************************************************************************/ + +const dng_image * dng_negative::RawTransparencyMask () const + { + + if (fRawTransparencyMask.Get ()) + { + + return fRawTransparencyMask.Get (); + + } + + return TransparencyMask (); + + } + +/*****************************************************************************/ + +uint32 dng_negative::RawTransparencyMaskBitDepth () const + { + + if (fRawTransparencyMaskBitDepth) + { + + return fRawTransparencyMaskBitDepth; + + } + + const dng_image *mask = RawTransparencyMask (); + + if (mask) + { + + switch (mask->PixelType ()) + { + + case ttByte: + return 8; + + case ttShort: + return 16; + + case ttFloat: + return 32; + + default: + ThrowProgramError (); + + } + + } + + return 0; + + } + +/*****************************************************************************/ + +void dng_negative::ReadTransparencyMask (dng_host &host, + dng_stream &stream, + dng_info &info) + { + + if (info.fMaskIndex != -1) + { + + // Allocate image we are reading. + + dng_ifd &maskIFD = *info.fIFD [info.fMaskIndex].Get (); + + fTransparencyMask.Reset (host.Make_dng_image (maskIFD.Bounds (), + 1, + maskIFD.PixelType ())); + + // Read the image. + + maskIFD.ReadImage (host, + stream, + *fTransparencyMask.Get ()); + + // Remember the pixel depth. + + fRawTransparencyMaskBitDepth = maskIFD.fBitsPerSample [0]; + + } + + } + +/*****************************************************************************/ + +void dng_negative::ResizeTransparencyToMatchStage3 (dng_host &host, + bool convertTo8Bit) + { + + if (TransparencyMask ()) + { + + if ((TransparencyMask ()->Bounds () != fStage3Image->Bounds ()) || + (TransparencyMask ()->PixelType () != ttByte && convertTo8Bit)) + { + + AutoPtr newMask (host.Make_dng_image (fStage3Image->Bounds (), + 1, + convertTo8Bit ? + ttByte : + TransparencyMask ()->PixelType ())); + + host.ResampleImage (*TransparencyMask (), + *newMask); + + fTransparencyMask.Reset (newMask.Release ()); + + if (!fRawTransparencyMask.Get ()) + { + fRawTransparencyMaskBitDepth = 0; + } + + } + + } + + } + +/*****************************************************************************/ + +bool dng_negative::NeedFlattenTransparency (dng_host & /* host */) + { + + if (TransparencyMask ()) + { + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_negative::FlattenTransparency (dng_host & /* host */) + { + + ThrowNotYetImplemented (); + + } + +/*****************************************************************************/ + +const dng_image * dng_negative::UnflattenedStage3Image () const + { + + if (fUnflattenedStage3Image.Get ()) + { + + return fUnflattenedStage3Image.Get (); + + } + + return fStage3Image.Get (); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_negative.h b/source/lib/dng_sdk/dng_negative.h new file mode 100644 index 0000000..1304292 --- /dev/null +++ b/source/lib/dng_sdk/dng_negative.h @@ -0,0 +1,2397 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_negative.h#4 $ */ +/* $DateTime: 2012/08/02 06:09:06 $ */ +/* $Change: 841096 $ */ +/* $Author: erichan $ */ + +/** \file + * Functions and classes for working with a digital negative (image data and + * corresponding metadata). + */ + +/*****************************************************************************/ + +#ifndef __dng_negative__ +#define __dng_negative__ + +/*****************************************************************************/ + +#include "dng_1d_function.h" +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_fingerprint.h" +#include "dng_image.h" +#include "dng_linearization_info.h" +#include "dng_matrix.h" +#include "dng_mosaic_info.h" +#include "dng_opcode_list.h" +#include "dng_orientation.h" +#include "dng_rational.h" +#include "dng_sdk_limits.h" +#include "dng_string.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_types.h" +#include "dng_utils.h" +#include "dng_xy_coord.h" + +#include + +/*****************************************************************************/ + +// To prevent using the internal metadata when we meant to use override +// metadata, the following definitions allow us to only allow access to +// the internal metadata on non-const negatives. This allows the old API +// to keep working essentially unchanged provided one does not use const +// negatives, but will prevent access to the embedded data on const +// negatives. + +#if 1 + +#define qMetadataOnConst 0 +#define METACONST + +#else + +#define qMetadataOnConst 1 +#define METACONST const + +#endif + +/*****************************************************************************/ + +#if GPR_READING + typedef bool (*Vc5DecodeCallback)(dng_stream &stream, dng_image &image ); +#endif + +/// \brief Noise model for photon and sensor read noise, assuming that they are +/// independent random variables and spatially invariant. +/// +/// The noise model is N (x) = sqrt (scale*x + offset), where x represents a linear +/// signal value in the range [0,1], and N (x) is the standard deviation (i.e., +/// noise). The parameters scale and offset are both sensor-dependent and +/// ISO-dependent. scale must be positive, and offset must be non-negative. + +class dng_noise_function: public dng_1d_function + { + + protected: + + real64 fScale; + real64 fOffset; + + public: + + /// Create empty and invalid noise function. + + dng_noise_function () + + : fScale (0.0) + , fOffset (0.0) + + { + + } + + /// Create noise function with the specified scale and offset. + + dng_noise_function (real64 scale, + real64 offset) + + : fScale (scale) + , fOffset (offset) + + { + + } + + /// Compute noise (standard deviation) at the specified average signal level + /// x. + + virtual real64 Evaluate (real64 x) const + { + return sqrt (fScale * x + fOffset); + } + + /// The scale (slope, gain) of the noise function. + + real64 Scale () const + { + return fScale; + } + + /// The offset (square of the noise floor) of the noise function. + + real64 Offset () const + { + return fOffset; + } + + /// Set the scale (slope, gain) of the noise function. + + void SetScale (real64 scale) + { + fScale = scale; + } + + /// Set the offset (square of the noise floor) of the noise function. + + void SetOffset (real64 offset) + { + fOffset = offset; + } + + /// Is the noise function valid? + + bool IsValid () const + { + return (fScale > 0.0 && fOffset >= 0.0); + } + + }; + +/*****************************************************************************/ + +/// \brief Noise profile for a negative. +/// +/// For mosaiced negatives, the noise profile describes the approximate noise +/// characteristics of a mosaic negative after linearization, but prior to +/// demosaicing. For demosaiced negatives (i.e., linear DNGs), the noise profile +/// describes the approximate noise characteristics of the image data immediately +/// following the demosaic step, prior to the processing of opcode list 3. +/// +/// A noise profile may contain 1 or N noise functions, where N is the number of +/// color planes for the negative. Otherwise the noise profile is considered to be +/// invalid for that negative. If the noise profile contains 1 noise function, then +/// it is assumed that this single noise function applies to all color planes of the +/// negative. Otherwise, the N noise functions map to the N planes of the negative in +/// order specified in the CFAPlaneColor tag. + +class dng_noise_profile + { + + protected: + + std::vector fNoiseFunctions; + + public: + + /// Create empty (invalid) noise profile. + + dng_noise_profile (); + + /// Create noise profile with the specified noise functions (1 per plane). + + explicit dng_noise_profile (const std::vector &functions); + + /// Is the noise profile valid? + + bool IsValid () const; + + /// Is the noise profile valid for the specified negative? + + bool IsValidForNegative (const dng_negative &negative) const; + + /// The noise function for the specified plane. + + const dng_noise_function & NoiseFunction (uint32 plane) const; + + /// The number of noise functions in this profile. + + uint32 NumFunctions () const; + + }; + +/*****************************************************************************/ + +/// \brief Main class for holding metadata. + +class dng_metadata + { + + private: + + // Base orientation of both the thumbnail and raw data. This is + // generally based on the EXIF values. + + bool fHasBaseOrientation; + + dng_orientation fBaseOrientation; + + // Is the maker note safe to copy from file to file? Defaults to false + // because many maker notes are not safe. + + bool fIsMakerNoteSafe; + + // MakerNote binary data block. + + AutoPtr fMakerNote; + + // EXIF data. + + AutoPtr fExif; + + // A copy of the EXIF data before is was synchronized with other metadata sources. + + AutoPtr fOriginalExif; + + // IPTC binary data block and offset in original file. + + AutoPtr fIPTCBlock; + + uint64 fIPTCOffset; + + // XMP data. + + AutoPtr fXMP; + + // If there a valid embedded XMP block, has is its digest? NULL if no valid + // embedded XMP. + + dng_fingerprint fEmbeddedXMPDigest; + + // Is the XMP data from a sidecar file? + + bool fXMPinSidecar; + + // If the XMP data is from a sidecar file, is the sidecar file newer + // than the raw file? + + bool fXMPisNewer; + + // Source file mimi-type, if known. + + dng_string fSourceMIMI; + + public: + + dng_metadata (dng_host &host); + + dng_metadata (const dng_metadata &rhs, + dng_memory_allocator &allocator); + + virtual ~dng_metadata (); + + /// Copy this metadata. + + virtual dng_metadata * Clone (dng_memory_allocator &allocator) const; + + /// Setter for BaseOrientation. + + void SetBaseOrientation (const dng_orientation &orientation); + + /// Has BaseOrientation been set? + + bool HasBaseOrientation () const + { + return fHasBaseOrientation; + } + + /// Getter for BaseOrientation. + + const dng_orientation & BaseOrientation () const + { + return fBaseOrientation; + } + + /// Logically rotates the image by changing the orientation values. + /// This will also update the XMP data. + + void ApplyOrientation (const dng_orientation &orientation); + + // API for IPTC metadata: + + void SetIPTC (AutoPtr &block, + uint64 offset); + + void SetIPTC (AutoPtr &block); + + void ClearIPTC (); + + const void * IPTCData () const; + + uint32 IPTCLength () const; + + uint64 IPTCOffset () const; + + dng_fingerprint IPTCDigest (bool includePadding = true) const; + + void RebuildIPTC (dng_memory_allocator &allocator, + bool padForTIFF); + + // API for MakerNote data: + + void SetMakerNoteSafety (bool safe) + { + fIsMakerNoteSafe = safe; + } + + bool IsMakerNoteSafe () const + { + return fIsMakerNoteSafe; + } + + void SetMakerNote (AutoPtr &block) + { + fMakerNote.Reset (block.Release ()); + } + + void ClearMakerNote () + { + fIsMakerNoteSafe = false; + fMakerNote.Reset (); + } + + const void * MakerNoteData () const + { + return fMakerNote.Get () ? fMakerNote->Buffer () + : NULL; + } + + uint32 MakerNoteLength () const + { + return fMakerNote.Get () ? fMakerNote->LogicalSize () + : 0; + } + + // API for EXIF metadata: + + dng_exif * GetExif () + { + return fExif.Get (); + } + + const dng_exif * GetExif () const + { + return fExif.Get (); + } + + template< class E > + E & Exif (); + + template< class E > + const E & Exif () const; + + void ResetExif (dng_exif * newExif); + + dng_memory_block * BuildExifBlock (dng_memory_allocator &allocator, + const dng_resolution *resolution = NULL, + bool includeIPTC = false, + const dng_jpeg_preview *thumbnail = NULL) const; + + // API for original EXIF metadata. + + dng_exif * GetOriginalExif () + { + return fOriginalExif.Get (); + } + + const dng_exif * GetOriginalExif () const + { + return fOriginalExif.Get (); + } + + // API for XMP metadata: + + bool SetXMP (dng_host &host, + const void *buffer, + uint32 count, + bool xmpInSidecar = false, + bool xmpIsNewer = false); + + void SetEmbeddedXMP (dng_host &host, + const void *buffer, + uint32 count); + + dng_xmp * GetXMP () + { + return fXMP.Get (); + } + + const dng_xmp * GetXMP () const + { + return fXMP.Get (); + } + + template< class X > + X & XMP (); + + template< class X > + const X & XMP () const; + + bool XMPinSidecar () const + { + return fXMPinSidecar; + } + + const dng_fingerprint & EmbeddedXMPDigest () const + { + return fEmbeddedXMPDigest; + } + + bool HaveValidEmbeddedXMP () const + { + return fEmbeddedXMPDigest.IsValid (); + } + + void ResetXMP (dng_xmp * newXMP); + + void ResetXMPSidecarNewer (dng_xmp * newXMP, bool inSidecar, bool isNewer ); + + // Synchronize metadata sources. + + void SynchronizeMetadata (); + + // Routines to update the date/time field in the EXIF and XMP + // metadata. + + void UpdateDateTime (const dng_date_time_info &dt); + + void UpdateDateTimeToNow (); + + void UpdateMetadataDateTimeToNow (); + + // Routines to set and get the source file MIMI type. + + void SetSourceMIMI (const char *s) + { + fSourceMIMI.Set (s); + } + + const dng_string & SourceMIMI () const + { + return fSourceMIMI; + } + + }; + +/*****************************************************************************/ + +template< class E > +E & dng_metadata::Exif () + { + dng_exif * exif = GetExif (); + if (!exif) ThrowProgramError ("EXIF object is NULL."); + return dynamic_cast< E & > (*exif); + } + +/*****************************************************************************/ + +template< class E > +const E & dng_metadata::Exif () const + { + const dng_exif * exif = GetExif (); + if (!exif) ThrowProgramError ("EXIF object is NULL."); + return dynamic_cast< const E & > (*exif); + } + +/*****************************************************************************/ + +template< class X > +X & dng_metadata::XMP () + { + dng_xmp * xmp = GetXMP (); + if (!xmp) ThrowProgramError ("XMP object is NULL."); + return dynamic_cast< X & > (*xmp); + } + +/*****************************************************************************/ + +template< class X > +const X & dng_metadata::XMP () const + { + const dng_xmp * xmp = GetXMP (); + if (!xmp) ThrowProgramError ("XMP object is NULL."); + return dynamic_cast< const X & > (*xmp); + } + +/*****************************************************************************/ + +/// \brief Main class for holding DNG image data and associated metadata. + +class dng_negative + { + + public: + + enum RawImageStageEnum + { + rawImageStagePreOpcode1, + rawImageStagePostOpcode1, + rawImageStagePostOpcode2, + rawImageStagePreOpcode3, + rawImageStagePostOpcode3, + rawImageStageNone + }; + + protected: + + // The negative stores an associated allocator. It does not do + // anything to keep it alive or to release it when the object destructs. + // Hence, clients will need to make sure that the allocator's lifespan + // encompasses that of the dng_factory object which is generally + // directly bound to the dng_negative object. + + dng_memory_allocator &fAllocator; + + // Non-localized ASCII model name. + + dng_string fModelName; + + // Localized UTF-8 model name. + + dng_string fLocalName; + + // The area of raw image that should be included in the final converted + // image. This stems from extra pixels around the edges of the sensor + // including both the black mask and some additional padding. + + // The default crop can be smaller than the "active" area which includes + // the padding but not the black masked pixels. + + dng_urational fDefaultCropSizeH; + dng_urational fDefaultCropSizeV; + + dng_urational fDefaultCropOriginH; + dng_urational fDefaultCropOriginV; + + // Default user crop, in relative coordinates. + + dng_urational fDefaultUserCropT; + dng_urational fDefaultUserCropL; + dng_urational fDefaultUserCropB; + dng_urational fDefaultUserCropR; + + // Default scale factors. Generally, 1.0 for square pixel cameras. They + // can compensate for non-square pixels. The choice of exact values will + // generally depend on what the camera does. These are particularly + // interesting for the Nikon D1X and the Fuji diamond mosaic. + + dng_urational fDefaultScaleH; + dng_urational fDefaultScaleV; + + // Best quality scale factor. Used for the Nikon D1X and Fuji cameras + // to force everything to be a scale up rather than scale down. So, + // generally this is 1.0 / min (fDefaultScaleH, fDefaultScaleV) but + // this isn't used if the scale factors are only slightly different + // from 1.0. + + dng_urational fBestQualityScale; + + // Proxy image support. Remember certain sizes for the original image + // this proxy was derived from. + + dng_point fOriginalDefaultFinalSize; + dng_point fOriginalBestQualityFinalSize; + + dng_urational fOriginalDefaultCropSizeH; + dng_urational fOriginalDefaultCropSizeV; + + // Scale factors used in demosaic algorithm (calculated). + // Maps raw image coordinates to full image coordinates -- i.e., + // original image coordinates on raw sensor data to coordinates + // in fStage3Image which is the output of the interpolation step. + // So, if we downsample when interpolating, these numbers get + // smaller. + + real64 fRawToFullScaleH; + real64 fRawToFullScaleV; + + // Relative amount of noise at ISO 100. This is measured per camera model + // based on looking at flat areas of color. + + dng_urational fBaselineNoise; + + // How much noise reduction has already been applied (0.0 to 1.0) to the + // the raw image data? 0.0 = none, 1.0 = "ideal" amount--i.e. don't apply any + // more by default. 0/0 for unknown. + + dng_urational fNoiseReductionApplied; + + // Amount of noise for this negative (see dng_noise_profile for details). + + dng_noise_profile fNoiseProfile; + + // Zero point for the exposure compensation slider. This reflects how + // the manufacturer sets up the camera and its conversions. + + dng_srational fBaselineExposure; + + // Relative amount of sharpening required. This is chosen per camera + // model based on how strong the anti-alias filter is on the camera + // and the quality of the lenses. This scales the sharpness slider + // value. + + dng_urational fBaselineSharpness; + + // Chroma blur radius (or 0/0 for auto). Set to 0/1 to disable + // chroma blurring. + + dng_urational fChromaBlurRadius; + + // Anti-alias filter strength (0.0 to 1.0). Used as a hint + // to the demosaic algorithms. + + dng_urational fAntiAliasStrength; + + // Linear response limit. The point at which the sensor goes + // non-linear and color information becomes unreliable. Used in + // the highlight-recovery logic. + + dng_urational fLinearResponseLimit; + + // Scale factor for shadows slider. The Fuji HDR cameras, for example, + // need a more sensitive shadow slider. + + dng_urational fShadowScale; + + // Colormetric reference. + + uint32 fColorimetricReference; + + // Number of color channels for this image (e.g. 1, 3, or 4). + + uint32 fColorChannels; + + // Amount by which each channel has already been scaled. Some cameras + // have analog amplifiers on the color channels and these can result + // in different scalings per channel. This provides some level of + // analog white balancing. The Nikon D1 also did digital scaling but + // this caused problems with highlight recovery. + + dng_vector fAnalogBalance; + + // The "As Shot" neutral color coordinates in native camera space. + // This overrides fCameraWhiteXY if both are specified. This + // specifies the values per channel that would result in a neutral + // color for the "As Shot" case. This is generally supplied by + // the camera. + + dng_vector fCameraNeutral; + + // The "As Shot" white balance xy coordinates. Sometimes this is + // supplied by the camera. Sometimes the camera just supplies a name + // for the white balance. + + dng_xy_coord fCameraWhiteXY; + + // Individual camera calibrations. + + // Camera data --> camera calibration --> "inverse" of color matrix + + // This will be a 4x4 matrix for a 4-color camera. The defaults are + // almost always the identity matrix and for the cases where they + // aren't, they are diagonal matrices. + + dng_matrix fCameraCalibration1; + dng_matrix fCameraCalibration2; + + // Signature which allows a profile to announce that it is compatible + // with these calibration matrices. + + dng_string fCameraCalibrationSignature; + + // List of camera profiles. + + std::vector fCameraProfile; + + // "As shot" camera profile name. + + dng_string fAsShotProfileName; + + // Raw image data digests. These are MD5 fingerprints of the raw image data + // in the file, computed using a specific algorithms. They can be used + // verify the raw data has not been corrupted. The new version is faster + // to compute on MP machines, and is used starting with DNG version 1.4. + + mutable dng_fingerprint fRawImageDigest; + + mutable dng_fingerprint fNewRawImageDigest; + + // Raw data unique ID. This is an unique identifer for the actual + // raw image data in the file. It can be used to index into caches + // for this data. + + mutable dng_fingerprint fRawDataUniqueID; + + // Original raw file name. Just the file name, not the full path. + + dng_string fOriginalRawFileName; + + // Is the original raw file data availaible? + + bool fHasOriginalRawFileData; + + // The compressed original raw file data. + + AutoPtr fOriginalRawFileData; + + // MD5 digest of original raw file data block. + + mutable dng_fingerprint fOriginalRawFileDigest; + + // DNG private data block. + + AutoPtr fDNGPrivateData; + + // Metadata information (XMP, IPTC, EXIF, orientation) + + dng_metadata fMetadata; + + // Information required to linearize and range map the raw data. + + AutoPtr fLinearizationInfo; + + // Information required to demoasic the raw data. + + AutoPtr fMosaicInfo; + + // Opcode list 1. (Applied to stored data) + + dng_opcode_list fOpcodeList1; + + // Opcode list 2. (Applied to range mapped data) + + dng_opcode_list fOpcodeList2; + + // Opcode list 3. (Post demosaic) + + dng_opcode_list fOpcodeList3; + + // Stage 1 image, which is image data stored in a DNG file. + + AutoPtr fStage1Image; + + // Stage 2 image, which is the stage 1 image after it has been + // linearized and range mapped. + + AutoPtr fStage2Image; + + // Stage 3 image, which is the stage 2 image after it has been + // demosaiced. + + AutoPtr fStage3Image; + + // Additiona gain applied when building the stage 3 image. + + real64 fStage3Gain; + + // Were any approximations (e.g. downsampling, etc.) applied + // file reading this image? + + bool fIsPreview; + + // Does the file appear to be damaged? + + bool fIsDamaged; + + // At what processing stage did we grab a copy of raw image data? + + RawImageStageEnum fRawImageStage; + + // The raw image data that we grabbed, if any. + + AutoPtr fRawImage; + + // The floating point bit depth of the raw file, if any. + + uint32 fRawFloatBitDepth; + + // The raw image JPEG data that we grabbed, if any. + + AutoPtr fRawJPEGImage; + + // Keep a separate digest for the compressed JPEG data, if any. + + mutable dng_fingerprint fRawJPEGImageDigest; + + // Transparency mask image, if any. + + AutoPtr fTransparencyMask; + + // Grabbed transparency mask, if we are not saving the current mask. + + AutoPtr fRawTransparencyMask; + + // The bit depth for the raw transparancy mask, if known. + + uint32 fRawTransparencyMaskBitDepth; + + // We sometimes need to keep of copy of the stage3 image before + // flattening the transparency. + + AutoPtr fUnflattenedStage3Image; + + public: + + virtual ~dng_negative (); + + static dng_negative * Make (dng_host &host); + + /// Provide access to the memory allocator used for this object. + + dng_memory_allocator & Allocator () const + { + return fAllocator; + } + + bool IsVc5Image(dng_info &info); + + void ReadVc5Image (dng_host &host, + dng_stream &stream, + dng_info &info, + dng_read_image &imageReader ); + + /// Getter for ModelName. + + void SetModelName (const char *name) + { + fModelName.Set_ASCII (name); + } + + /// Setter for ModelName. + + const dng_string & ModelName () const + { + return fModelName; + } + + /// Setter for LocalName. + + void SetLocalName (const char *name) + { + fLocalName.Set (name); + } + + /// Getter for LocalName. + + const dng_string & LocalName () const + { + return fLocalName; + } + + /// Getter for metadata + + dng_metadata &Metadata () + { + return fMetadata; + } + + #if qMetadataOnConst + + const dng_metadata &Metadata () const + { + return fMetadata; + } + + #endif // qMetadataOnConst + + /// Make a copy of the internal metadata generally as a basis for further + /// changes. + + dng_metadata * CloneInternalMetadata () const; + + protected: + + /// An accessor for the internal metadata that works even when we + /// have general access turned off. This is needed to provide + /// access to EXIF ISO information. + + const dng_metadata &InternalMetadata () const + { + return fMetadata; + } + + public: + + /// Setter for BaseOrientation. + + void SetBaseOrientation (const dng_orientation &orientation) + { + Metadata ().SetBaseOrientation (orientation); + } + + /// Has BaseOrientation been set? + + bool HasBaseOrientation () METACONST + { + return Metadata ().HasBaseOrientation (); + } + + /// Getter for BaseOrientation. + + const dng_orientation & BaseOrientation () METACONST + { + return Metadata ().BaseOrientation (); + } + + /// Hook to allow SDK host code to add additional rotations. + + virtual dng_orientation ComputeOrientation (const dng_metadata &metadata) const; + + /// For non-const negatives, we simply default to using the metadata attached to the negative. + + dng_orientation Orientation () + { + return ComputeOrientation (Metadata ()); + } + + /// Logically rotates the image by changing the orientation values. + /// This will also update the XMP data. + + void ApplyOrientation (const dng_orientation &orientation) + { + Metadata ().ApplyOrientation (orientation); + } + + /// Setter for DefaultCropSize. + + void SetDefaultCropSize (const dng_urational &sizeH, + const dng_urational &sizeV) + { + fDefaultCropSizeH = sizeH; + fDefaultCropSizeV = sizeV; + } + + /// Setter for DefaultCropSize. + + void SetDefaultCropSize (uint32 sizeH, + uint32 sizeV) + { + SetDefaultCropSize (dng_urational (sizeH, 1), + dng_urational (sizeV, 1)); + } + + /// Getter for DefaultCropSize horizontal. + + const dng_urational & DefaultCropSizeH () const + { + return fDefaultCropSizeH; + } + + /// Getter for DefaultCropSize vertical. + + const dng_urational & DefaultCropSizeV () const + { + return fDefaultCropSizeV; + } + + /// Setter for DefaultCropOrigin. + + void SetDefaultCropOrigin (const dng_urational &originH, + const dng_urational &originV) + { + fDefaultCropOriginH = originH; + fDefaultCropOriginV = originV; + } + + /// Setter for DefaultCropOrigin. + + void SetDefaultCropOrigin (uint32 originH, + uint32 originV) + { + SetDefaultCropOrigin (dng_urational (originH, 1), + dng_urational (originV, 1)); + } + + /// Set default crop around center of image. + + void SetDefaultCropCentered (const dng_point &rawSize) + { + + uint32 sizeH = Round_uint32 (fDefaultCropSizeH.As_real64 ()); + uint32 sizeV = Round_uint32 (fDefaultCropSizeV.As_real64 ()); + + SetDefaultCropOrigin ((rawSize.h - sizeH) >> 1, + (rawSize.v - sizeV) >> 1); + + } + + /// Get default crop origin horizontal value. + + const dng_urational & DefaultCropOriginH () const + { + return fDefaultCropOriginH; + } + + /// Get default crop origin vertical value. + + const dng_urational & DefaultCropOriginV () const + { + return fDefaultCropOriginV; + } + + /// Getter for top coordinate of default user crop. + + const dng_urational & DefaultUserCropT () const + { + return fDefaultUserCropT; + } + + /// Getter for left coordinate of default user crop. + + const dng_urational & DefaultUserCropL () const + { + return fDefaultUserCropL; + } + + /// Getter for bottom coordinate of default user crop. + + const dng_urational & DefaultUserCropB () const + { + return fDefaultUserCropB; + } + + /// Getter for right coordinate of default user crop. + + const dng_urational & DefaultUserCropR () const + { + return fDefaultUserCropR; + } + + /// Reset default user crop to default crop area. + + void ResetDefaultUserCrop () + { + fDefaultUserCropT = dng_urational (0, 1); + fDefaultUserCropL = dng_urational (0, 1); + fDefaultUserCropB = dng_urational (1, 1); + fDefaultUserCropR = dng_urational (1, 1); + } + + /// Setter for all 4 coordinates of default user crop. + + void SetDefaultUserCrop (const dng_urational &t, + const dng_urational &l, + const dng_urational &b, + const dng_urational &r) + { + fDefaultUserCropT = t; + fDefaultUserCropL = l; + fDefaultUserCropB = b; + fDefaultUserCropR = r; + } + + /// Setter for top coordinate of default user crop. + + void SetDefaultUserCropT (const dng_urational &value) + { + fDefaultUserCropT = value; + } + + /// Setter for left coordinate of default user crop. + + void SetDefaultUserCropL (const dng_urational &value) + { + fDefaultUserCropL = value; + } + + /// Setter for bottom coordinate of default user crop. + + void SetDefaultUserCropB (const dng_urational &value) + { + fDefaultUserCropB = value; + } + + /// Setter for right coordinate of default user crop. + + void SetDefaultUserCropR (const dng_urational &value) + { + fDefaultUserCropR = value; + } + + /// Setter for DefaultScale. + + void SetDefaultScale (const dng_urational &scaleH, + const dng_urational &scaleV) + { + fDefaultScaleH = scaleH; + fDefaultScaleV = scaleV; + } + + /// Get default scale horizontal value. + + const dng_urational & DefaultScaleH () const + { + return fDefaultScaleH; + } + + /// Get default scale vertical value. + + const dng_urational & DefaultScaleV () const + { + return fDefaultScaleV; + } + + /// Setter for BestQualityScale. + + void SetBestQualityScale (const dng_urational &scale) + { + fBestQualityScale = scale; + } + + /// Getter for BestQualityScale. + + const dng_urational & BestQualityScale () const + { + return fBestQualityScale; + } + + /// API for raw to full image scaling factors horizontal. + + real64 RawToFullScaleH () const + { + return fRawToFullScaleH; + } + + /// API for raw to full image scaling factors vertical. + + real64 RawToFullScaleV () const + { + return fRawToFullScaleV; + } + + /// Setter for raw to full scales. + + void SetRawToFullScale (real64 scaleH, + real64 scaleV) + { + fRawToFullScaleH = scaleH; + fRawToFullScaleV = scaleV; + } + + /// Get default scale factor. + /// When specifing a single scale factor, we use the horizontal + /// scale factor, and let the vertical scale factor be calculated + /// based on the pixel aspect ratio. + + real64 DefaultScale () const + { + return DefaultScaleH ().As_real64 (); + } + + /// Default cropped image size (at scale == 1.0) width. + + real64 SquareWidth () const + { + return DefaultCropSizeH ().As_real64 (); + } + + /// Default cropped image size (at scale == 1.0) height. + + real64 SquareHeight () const + { + return DefaultCropSizeV ().As_real64 () * + DefaultScaleV ().As_real64 () / + DefaultScaleH ().As_real64 (); + } + + /// Default cropped image aspect ratio. + + real64 AspectRatio () const + { + return SquareWidth () / + SquareHeight (); + } + + /// Pixel aspect ratio of stage 3 image. + + real64 PixelAspectRatio () const + { + return (DefaultScaleH ().As_real64 () / RawToFullScaleH ()) / + (DefaultScaleV ().As_real64 () / RawToFullScaleV ()); + } + + /// Default cropped image size at given scale factor width. + + uint32 FinalWidth (real64 scale) const + { + return Round_uint32 (SquareWidth () * scale); + } + + /// Default cropped image size at given scale factor height. + + uint32 FinalHeight (real64 scale) const + { + return Round_uint32 (SquareHeight () * scale); + } + + /// Default cropped image size at default scale factor width. + + uint32 DefaultFinalWidth () const + { + return FinalWidth (DefaultScale ()); + } + + /// Default cropped image size at default scale factor height. + + uint32 DefaultFinalHeight () const + { + return FinalHeight (DefaultScale ()); + } + + /// Get best quality width. + /// For a naive conversion, one could use either the default size, + /// or the best quality size. + + uint32 BestQualityFinalWidth () const + { + return FinalWidth (DefaultScale () * BestQualityScale ().As_real64 ()); + } + + /// Get best quality height. + /// For a naive conversion, one could use either the default size, + /// or the best quality size. + + uint32 BestQualityFinalHeight () const + { + return FinalHeight (DefaultScale () * BestQualityScale ().As_real64 ()); + } + + /// Default size of original (non-proxy) image. For non-proxy images, this + /// is equal to DefaultFinalWidth/DefaultFinalHight. For proxy images, this + /// is equal to the DefaultFinalWidth/DefaultFinalHeight of the image this + /// proxy was derived from. + + const dng_point & OriginalDefaultFinalSize () const + { + return fOriginalDefaultFinalSize; + } + + /// Setter for OriginalDefaultFinalSize. + + void SetOriginalDefaultFinalSize (const dng_point &size) + { + fOriginalDefaultFinalSize = size; + } + + /// Best quality size of original (non-proxy) image. For non-proxy images, this + /// is equal to BestQualityFinalWidth/BestQualityFinalHeight. For proxy images, this + /// is equal to the BestQualityFinalWidth/BestQualityFinalHeight of the image this + /// proxy was derived from. + + const dng_point & OriginalBestQualityFinalSize () const + { + return fOriginalBestQualityFinalSize; + } + + /// Setter for OriginalBestQualityFinalSize. + + void SetOriginalBestQualityFinalSize (const dng_point &size) + { + fOriginalBestQualityFinalSize = size; + } + + /// DefaultCropSize for original (non-proxy) image. For non-proxy images, + /// this is equal to the DefaultCropSize. for proxy images, this is + /// equal size of the DefaultCropSize of the image this proxy was derived from. + + const dng_urational & OriginalDefaultCropSizeH () const + { + return fOriginalDefaultCropSizeH; + } + + const dng_urational & OriginalDefaultCropSizeV () const + { + return fOriginalDefaultCropSizeV; + } + + /// Setter for OriginalDefaultCropSize. + + void SetOriginalDefaultCropSize (const dng_urational &sizeH, + const dng_urational &sizeV) + { + fOriginalDefaultCropSizeH = sizeH; + fOriginalDefaultCropSizeV = sizeV; + } + + /// If the original size fields are undefined, set them to the + /// current sizes. + + void SetDefaultOriginalSizes (); + + /// The default crop area in the stage 3 image coordinates. + + dng_rect DefaultCropArea () const; + + /// Setter for BaselineNoise. + + void SetBaselineNoise (real64 noise) + { + fBaselineNoise.Set_real64 (noise, 100); + } + + /// Getter for BaselineNoise as dng_urational. + + const dng_urational & BaselineNoiseR () const + { + return fBaselineNoise; + } + + /// Getter for BaselineNoise as real64. + + real64 BaselineNoise () const + { + return fBaselineNoise.As_real64 (); + } + + /// Setter for NoiseReductionApplied. + + void SetNoiseReductionApplied (const dng_urational &value) + { + fNoiseReductionApplied = value; + } + + /// Getter for NoiseReductionApplied. + + const dng_urational & NoiseReductionApplied () const + { + return fNoiseReductionApplied; + } + + /// Setter for noise profile. + + void SetNoiseProfile (const dng_noise_profile &noiseProfile) + { + fNoiseProfile = noiseProfile; + } + + /// Does this negative have a valid noise profile? + + bool HasNoiseProfile () const + { + return fNoiseProfile.IsValidForNegative (*this); + } + + /// Getter for noise profile. + + const dng_noise_profile & NoiseProfile () const + { + return fNoiseProfile; + } + + /// Setter for BaselineExposure. + + void SetBaselineExposure (real64 exposure) + { + fBaselineExposure.Set_real64 (exposure, 100); + } + + /// Getter for BaselineExposure as dng_urational. + + const dng_srational & BaselineExposureR () const + { + return fBaselineExposure; + } + + /// Getter for BaselineExposure as real64. + + real64 BaselineExposure () const + { + return BaselineExposureR ().As_real64 (); + } + + /// Compute total baseline exposure (sum of negative's BaselineExposure and + /// profile's BaselineExposureOffset). + + real64 TotalBaselineExposure (const dng_camera_profile_id &profileID) const; + + /// Setter for BaselineSharpness. + + void SetBaselineSharpness (real64 sharpness) + { + fBaselineSharpness.Set_real64 (sharpness, 100); + } + + /// Getter for BaselineSharpness as dng_urational. + + const dng_urational & BaselineSharpnessR () const + { + return fBaselineSharpness; + } + + /// Getter for BaselineSharpness as real64. + + real64 BaselineSharpness () const + { + return BaselineSharpnessR ().As_real64 (); + } + + /// Setter for ChromaBlurRadius. + + void SetChromaBlurRadius (const dng_urational &radius) + { + fChromaBlurRadius = radius; + } + + /// Getter for ChromaBlurRadius as dng_urational. + + const dng_urational & ChromaBlurRadius () const + { + return fChromaBlurRadius; + } + + /// Setter for AntiAliasStrength. + + void SetAntiAliasStrength (const dng_urational &strength) + { + fAntiAliasStrength = strength; + } + + /// Getter for AntiAliasStrength as dng_urational. + + const dng_urational & AntiAliasStrength () const + { + return fAntiAliasStrength; + } + + /// Setter for LinearResponseLimit. + + void SetLinearResponseLimit (real64 limit) + { + fLinearResponseLimit.Set_real64 (limit, 100); + } + + /// Getter for LinearResponseLimit as dng_urational. + + const dng_urational & LinearResponseLimitR () const + { + return fLinearResponseLimit; + } + + /// Getter for LinearResponseLimit as real64. + + real64 LinearResponseLimit () const + { + return LinearResponseLimitR ().As_real64 (); + } + + /// Setter for ShadowScale. + + void SetShadowScale (const dng_urational &scale); + + /// Getter for ShadowScale as dng_urational. + + const dng_urational & ShadowScaleR () const + { + return fShadowScale; + } + + /// Getter for ShadowScale as real64. + + real64 ShadowScale () const + { + return ShadowScaleR ().As_real64 (); + } + + // API for ColorimetricReference. + + void SetColorimetricReference (uint32 ref) + { + fColorimetricReference = ref; + } + + uint32 ColorimetricReference () const + { + return fColorimetricReference; + } + + /// Setter for ColorChannels. + + void SetColorChannels (uint32 channels) + { + fColorChannels = channels; + } + + /// Getter for ColorChannels. + + uint32 ColorChannels () const + { + return fColorChannels; + } + + /// Setter for Monochrome. + + void SetMonochrome () + { + SetColorChannels (1); + } + + /// Getter for Monochrome. + + bool IsMonochrome () const + { + return ColorChannels () == 1; + } + + /// Setter for AnalogBalance. + + void SetAnalogBalance (const dng_vector &b); + + /// Getter for AnalogBalance as dng_urational. + + dng_urational AnalogBalanceR (uint32 channel) const; + + /// Getter for AnalogBalance as real64. + + real64 AnalogBalance (uint32 channel) const; + + /// Setter for CameraNeutral. + + void SetCameraNeutral (const dng_vector &n); + + /// Clear CameraNeutral. + + void ClearCameraNeutral () + { + fCameraNeutral.Clear (); + } + + /// Determine if CameraNeutral has been set but not cleared. + + bool HasCameraNeutral () const + { + return fCameraNeutral.NotEmpty (); + } + + /// Getter for CameraNeutral. + + const dng_vector & CameraNeutral () const + { + return fCameraNeutral; + } + + dng_urational CameraNeutralR (uint32 channel) const; + + /// Setter for CameraWhiteXY. + + void SetCameraWhiteXY (const dng_xy_coord &coord); + + bool HasCameraWhiteXY () const + { + return fCameraWhiteXY.IsValid (); + } + + const dng_xy_coord & CameraWhiteXY () const; + + void GetCameraWhiteXY (dng_urational &x, + dng_urational &y) const; + + // API for camera calibration: + + /// Setter for first of up to two color matrices used for individual camera calibrations. + /// + /// The sequence of matrix transforms is: + /// Camera data --> camera calibration --> "inverse" of color matrix + /// + /// This will be a 4x4 matrix for a four-color camera. The defaults are + /// almost always the identity matrix, and for the cases where they + /// aren't, they are diagonal matrices. + + void SetCameraCalibration1 (const dng_matrix &m); + + /// Setter for second of up to two color matrices used for individual camera calibrations. + /// + /// The sequence of matrix transforms is: + /// Camera data --> camera calibration --> "inverse" of color matrix + /// + /// This will be a 4x4 matrix for a four-color camera. The defaults are + /// almost always the identity matrix, and for the cases where they + /// aren't, they are diagonal matrices. + + void SetCameraCalibration2 (const dng_matrix &m); + + /// Getter for first of up to two color matrices used for individual camera calibrations. + + const dng_matrix & CameraCalibration1 () const + { + return fCameraCalibration1; + } + + /// Getter for second of up to two color matrices used for individual camera calibrations. + + const dng_matrix & CameraCalibration2 () const + { + return fCameraCalibration2; + } + + void SetCameraCalibrationSignature (const char *signature) + { + fCameraCalibrationSignature.Set (signature); + } + + const dng_string & CameraCalibrationSignature () const + { + return fCameraCalibrationSignature; + } + + // Camera Profile API: + + void AddProfile (AutoPtr &profile); + + void ClearProfiles (); + + void ClearProfiles (bool clearBuiltinMatrixProfiles, + bool clearReadFromDisk); + + uint32 ProfileCount () const; + + const dng_camera_profile & ProfileByIndex (uint32 index) const; + + virtual const dng_camera_profile * ProfileByID (const dng_camera_profile_id &id, + bool useDefaultIfNoMatch = true) const; + + bool HasProfileID (const dng_camera_profile_id &id) const + { + return ProfileByID (id, false) != NULL; + } + + // Returns the camera profile to embed when saving to DNG: + + virtual const dng_camera_profile * ComputeCameraProfileToEmbed + (const dng_metadata &metadata) const; + + // For non-const negatives, we can use the embedded metadata. + + const dng_camera_profile * CameraProfileToEmbed () + { + return ComputeCameraProfileToEmbed (Metadata ()); + } + + // API for AsShotProfileName. + + void SetAsShotProfileName (const char *name) + { + fAsShotProfileName.Set (name); + } + + const dng_string & AsShotProfileName () const + { + return fAsShotProfileName; + } + + // Makes a dng_color_spec object for this negative. + + virtual dng_color_spec * MakeColorSpec (const dng_camera_profile_id &id) const; + + // Compute a MD5 hash on an image, using a fixed algorithm. + // The results must be stable across different hardware, OSes, + // and software versions. + + dng_fingerprint FindImageDigest (dng_host &host, + const dng_image &image) const; + + // API for RawImageDigest and NewRawImageDigest: + + void SetRawImageDigest (const dng_fingerprint &digest) + { + fRawImageDigest = digest; + } + + void SetNewRawImageDigest (const dng_fingerprint &digest) + { + fNewRawImageDigest = digest; + } + + void ClearRawImageDigest () const + { + fRawImageDigest .Clear (); + fNewRawImageDigest.Clear (); + } + + const dng_fingerprint & RawImageDigest () const + { + return fRawImageDigest; + } + + const dng_fingerprint & NewRawImageDigest () const + { + return fNewRawImageDigest; + } + + void FindRawImageDigest (dng_host &host) const; + + void FindNewRawImageDigest (dng_host &host) const; + + void ValidateRawImageDigest (dng_host &host); + + // API for RawDataUniqueID: + + void SetRawDataUniqueID (const dng_fingerprint &id) + { + fRawDataUniqueID = id; + } + + const dng_fingerprint & RawDataUniqueID () const + { + return fRawDataUniqueID; + } + + void FindRawDataUniqueID (dng_host &host) const; + + void RecomputeRawDataUniqueID (dng_host &host); + + // API for original raw file name: + + void SetOriginalRawFileName (const char *name) + { + fOriginalRawFileName.Set (name); + } + + bool HasOriginalRawFileName () const + { + return fOriginalRawFileName.NotEmpty (); + } + + const dng_string & OriginalRawFileName () const + { + return fOriginalRawFileName; + } + + // API for original raw file data: + + void SetHasOriginalRawFileData (bool hasData) + { + fHasOriginalRawFileData = hasData; + } + + bool CanEmbedOriginalRaw () const + { + return fHasOriginalRawFileData && HasOriginalRawFileName (); + } + + void SetOriginalRawFileData (AutoPtr &data) + { + fOriginalRawFileData.Reset (data.Release ()); + } + + const void * OriginalRawFileData () const + { + return fOriginalRawFileData.Get () ? fOriginalRawFileData->Buffer () + : NULL; + } + + uint32 OriginalRawFileDataLength () const + { + return fOriginalRawFileData.Get () ? fOriginalRawFileData->LogicalSize () + : 0; + } + + // API for original raw file data digest. + + void SetOriginalRawFileDigest (const dng_fingerprint &digest) + { + fOriginalRawFileDigest = digest; + } + + const dng_fingerprint & OriginalRawFileDigest () const + { + return fOriginalRawFileDigest; + } + + void FindOriginalRawFileDigest () const; + + void ValidateOriginalRawFileDigest (); + + // API for DNG private data: + + void SetPrivateData (AutoPtr &block) + { + fDNGPrivateData.Reset (block.Release ()); + } + + void ClearPrivateData () + { + fDNGPrivateData.Reset (); + } + + const uint8 * PrivateData () const + { + return fDNGPrivateData.Get () ? fDNGPrivateData->Buffer_uint8 () + : NULL; + } + + uint32 PrivateLength () const + { + return fDNGPrivateData.Get () ? fDNGPrivateData->LogicalSize () + : 0; + } + + // API for MakerNote data: + + void SetMakerNoteSafety (bool safe) + { + Metadata ().SetMakerNoteSafety (safe); + } + + bool IsMakerNoteSafe () METACONST + { + return Metadata ().IsMakerNoteSafe (); + } + + void SetMakerNote (AutoPtr &block) + { + Metadata ().SetMakerNote (block); + } + + void ClearMakerNote () + { + Metadata ().ClearMakerNote (); + } + + const void * MakerNoteData () METACONST + { + return Metadata ().MakerNoteData (); + } + + uint32 MakerNoteLength () METACONST + { + return Metadata ().MakerNoteLength (); + } + + // API for EXIF metadata: + + dng_exif * GetExif () + { + return Metadata ().GetExif (); + } + + #if qMetadataOnConst + + const dng_exif * GetExif () const + { + return Metadata ().GetExif (); + } + + #endif // qMetadataOnConst + + void ResetExif (dng_exif * newExif) + { + Metadata ().ResetExif (newExif); + } + + // API for original EXIF metadata. + + dng_exif * GetOriginalExif () + { + return Metadata ().GetOriginalExif (); + } + + #if qMetadataOnConst + + const dng_exif * GetOriginalExif () const + { + return Metadata ().GetOriginalExif (); + } + + #endif // qMetadataOnConst + + // API for IPTC metadata: + + void SetIPTC (AutoPtr &block, + uint64 offset) + { + Metadata ().SetIPTC (block, offset); + } + + void SetIPTC (AutoPtr &block) + { + Metadata ().SetIPTC (block); + } + + void ClearIPTC () + { + Metadata ().ClearIPTC (); + } + + const void * IPTCData () METACONST + { + return Metadata ().IPTCData (); + } + + uint32 IPTCLength () METACONST + { + return Metadata ().IPTCLength (); + } + + uint64 IPTCOffset () METACONST + { + return Metadata ().IPTCOffset (); + } + + dng_fingerprint IPTCDigest (bool includePadding = true) METACONST + { + return Metadata ().IPTCDigest (includePadding); + } + + void RebuildIPTC (bool padForTIFF) + { + Metadata ().RebuildIPTC (Allocator (), padForTIFF); + } + + // API for XMP metadata: + + bool SetXMP (dng_host &host, + const void *buffer, + uint32 count, + bool xmpInSidecar = false, + bool xmpIsNewer = false) + { + return Metadata ().SetXMP (host, + buffer, + count, + xmpInSidecar, + xmpIsNewer); + } + + dng_xmp * GetXMP () + { + return Metadata ().GetXMP (); + } + + #if qMetadataOnConst + + const dng_xmp * GetXMP () const + { + return Metadata ().GetXMP (); + } + + #endif // qMetadataOnConst + + bool XMPinSidecar () METACONST + { + return Metadata ().XMPinSidecar (); + } + + void ResetXMP (dng_xmp * newXMP) + { + Metadata ().ResetXMP (newXMP); + } + + void ResetXMPSidecarNewer (dng_xmp * newXMP, bool inSidecar, bool isNewer ) + { + Metadata ().ResetXMPSidecarNewer (newXMP, inSidecar, isNewer); + } + + bool HaveValidEmbeddedXMP () METACONST + { + return Metadata ().HaveValidEmbeddedXMP (); + } + + // API for source MIMI type. + + void SetSourceMIMI (const char *s) + { + Metadata ().SetSourceMIMI (s); + } + + // API for linearization information: + + const dng_linearization_info * GetLinearizationInfo () const + { + return fLinearizationInfo.Get (); + } + + void ClearLinearizationInfo () + { + fLinearizationInfo.Reset (); + } + + // Linearization curve. Usually used to increase compression ratios + // by storing the compressed data in a more visually uniform space. + // This is a 16-bit LUT that maps the stored data back to linear. + + void SetLinearization (AutoPtr &curve); + + // Active area (non-black masked pixels). These pixels are trimmed + // during linearization step. + + void SetActiveArea (const dng_rect &area); + + // Areas that are known to contain black masked pixels that can + // be used to estimate black levels. + + void SetMaskedAreas (uint32 count, + const dng_rect *area); + + void SetMaskedArea (const dng_rect &area) + { + SetMaskedAreas (1, &area); + } + + // Sensor black level information. + + void SetBlackLevel (real64 black, + int32 plane = -1); + + void SetQuadBlacks (real64 black0, + real64 black1, + real64 black2, + real64 black3, + int32 plane = -1); + + void SetRowBlacks (const real64 *blacks, + uint32 count); + + void SetColumnBlacks (const real64 *blacks, + uint32 count); + + // Sensor white level information. + + uint32 WhiteLevel (uint32 plane = 0) const; + + void SetWhiteLevel (uint32 white, + int32 plane = -1); + + // API for mosaic information: + + const dng_mosaic_info * GetMosaicInfo () const + { + return fMosaicInfo.Get (); + } + + void ClearMosaicInfo () + { + fMosaicInfo.Reset (); + } + + // ColorKeys APIs: + + void SetColorKeys (ColorKeyCode color0, + ColorKeyCode color1, + ColorKeyCode color2, + ColorKeyCode color3 = colorKeyMaxEnum); + + void SetRGB () + { + + SetColorChannels (3); + + SetColorKeys (colorKeyRed, + colorKeyGreen, + colorKeyBlue); + + } + + void SetCMY () + { + + SetColorChannels (3); + + SetColorKeys (colorKeyCyan, + colorKeyMagenta, + colorKeyYellow); + + } + + void SetGMCY () + { + + SetColorChannels (4); + + SetColorKeys (colorKeyGreen, + colorKeyMagenta, + colorKeyCyan, + colorKeyYellow); + + } + + // APIs to set mosaic patterns. + + void SetBayerMosaic (uint32 phase); + + void SetFujiMosaic (uint32 phase); + + void SetFujiMosaic6x6 (uint32 phase); + + void SetQuadMosaic (uint32 pattern); + + // BayerGreenSplit. + + void SetGreenSplit (uint32 split); + + // APIs for opcode lists. + + const dng_opcode_list & OpcodeList1 () const + { + return fOpcodeList1; + } + + dng_opcode_list & OpcodeList1 () + { + return fOpcodeList1; + } + + const dng_opcode_list & OpcodeList2 () const + { + return fOpcodeList2; + } + + dng_opcode_list & OpcodeList2 () + { + return fOpcodeList2; + } + + const dng_opcode_list & OpcodeList3 () const + { + return fOpcodeList3; + } + + dng_opcode_list & OpcodeList3 () + { + return fOpcodeList3; + } + + // First part of parsing logic. + + virtual void Parse (dng_host &host, + dng_stream &stream, + dng_info &info); + + // Second part of parsing logic. This is split off from the + // first part because these operations are useful when extending + // this sdk to support non-DNG raw formats. + + virtual void PostParse (dng_host &host, + dng_stream &stream, + dng_info &info); + + // Synchronize metadata sources. + + void SynchronizeMetadata () + { + Metadata ().SynchronizeMetadata (); + } + + // Routines to update the date/time field in the EXIF and XMP + // metadata. + + void UpdateDateTime (const dng_date_time_info &dt) + { + Metadata ().UpdateDateTime (dt); + } + + void UpdateDateTimeToNow () + { + Metadata ().UpdateDateTimeToNow (); + } + + // Developer's utility function to switch to four color Bayer + // interpolation. This is useful for evaluating how much green + // split a Bayer pattern sensor has. + + virtual bool SetFourColorBayer (); + + // Access routines for the image stages. + + const dng_image * Stage1Image () const + { + return fStage1Image.Get (); + } + + const dng_image * Stage2Image () const + { + return fStage2Image.Get (); + } + + const dng_image * Stage3Image () const + { + return fStage3Image.Get (); + } + + // Returns the processing stage of the raw image data. + + RawImageStageEnum RawImageStage () const + { + return fRawImageStage; + } + + // Returns the raw image data. + + const dng_image & RawImage () const; + + // API for raw floating point bit depth. + + uint32 RawFloatBitDepth () const + { + return fRawFloatBitDepth; + } + + void SetRawFloatBitDepth (uint32 bitDepth) + { + fRawFloatBitDepth = bitDepth; + } + + // API for raw jpeg image. + + const dng_jpeg_image * RawJPEGImage () const; + + void SetRawJPEGImage (AutoPtr &jpegImage); + + void ClearRawJPEGImage (); + + // API for RawJPEGImageDigest: + + void SetRawJPEGImageDigest (const dng_fingerprint &digest) + { + fRawJPEGImageDigest = digest; + } + + void ClearRawJPEGImageDigest () const + { + fRawJPEGImageDigest.Clear (); + } + + const dng_fingerprint & RawJPEGImageDigest () const + { + return fRawJPEGImageDigest; + } + + void FindRawJPEGImageDigest (dng_host &host) const; + + // Read the stage 1 image. + + virtual void ReadStage1Image (dng_host &host, + dng_stream &stream, + dng_info &info); + + // Assign the stage 1 image. + + void SetStage1Image (AutoPtr &image); + + // Assign the stage 2 image. + + void SetStage2Image (AutoPtr &image); + + // Assign the stage 3 image. + + void SetStage3Image (AutoPtr &image); + + // Build the stage 2 (linearized and range mapped) image. + + void BuildStage2Image (dng_host &host); + + // Build the stage 3 (demosaiced) image. + + void BuildStage3Image (dng_host &host, + int32 srcPlane = -1); + + // Additional gain applied when building the stage 3 image. + + void SetStage3Gain (real64 gain) + { + fStage3Gain = gain; + } + + real64 Stage3Gain () const + { + return fStage3Gain; + } + + // Adaptively encode a proxy image down to 8-bits/channel. + + dng_image * EncodeRawProxy (dng_host &host, + const dng_image &srcImage, + dng_opcode_list &opcodeList) const; + + // Convert to a proxy negative. + + void ConvertToProxy (dng_host &host, + dng_image_writer &writer, + uint32 proxySize = 0, + uint64 proxyCount = 0); + + // IsPreview API: + + void SetIsPreview (bool preview) + { + fIsPreview = preview; + } + + bool IsPreview () const + { + return fIsPreview; + } + + // IsDamaged API: + + void SetIsDamaged (bool damaged) + { + fIsDamaged = damaged; + } + + bool IsDamaged () const + { + return fIsDamaged; + } + + // Transparancy Mask API: + + void SetTransparencyMask (AutoPtr &image, + uint32 bitDepth = 0); + + const dng_image * TransparencyMask () const; + + const dng_image * RawTransparencyMask () const; + + uint32 RawTransparencyMaskBitDepth () const; + + void ReadTransparencyMask (dng_host &host, + dng_stream &stream, + dng_info &info); + + virtual bool NeedFlattenTransparency (dng_host &host); + + virtual void FlattenTransparency (dng_host &host); + + const dng_image * UnflattenedStage3Image () const; + + protected: + + dng_negative (dng_host &host); + + virtual void Initialize (); + + virtual dng_linearization_info * MakeLinearizationInfo (); + + void NeedLinearizationInfo (); + + virtual dng_mosaic_info * MakeMosaicInfo (); + + void NeedMosaicInfo (); + + virtual void DoBuildStage2 (dng_host &host); + + virtual void DoPostOpcodeList2 (dng_host &host); + + virtual bool NeedDefloatStage2 (dng_host &host); + + virtual void DefloatStage2 (dng_host &host); + + virtual void DoInterpolateStage3 (dng_host &host, + int32 srcPlane); + + virtual void DoMergeStage3 (dng_host &host); + + virtual void DoBuildStage3 (dng_host &host, + int32 srcPlane); + + virtual void AdjustProfileForStage3 (); + + virtual void ResizeTransparencyToMatchStage3 (dng_host &host, + bool convertTo8Bit = false); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_opcode_list.cpp b/source/lib/dng_sdk/dng_opcode_list.cpp new file mode 100644 index 0000000..a5af89a --- /dev/null +++ b/source/lib/dng_sdk/dng_opcode_list.cpp @@ -0,0 +1,274 @@ +/*****************************************************************************/ +// Copyright 2008-2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_opcode_list.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_opcode_list.h" + +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_memory_stream.h" +#include "dng_negative.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +#include + +/*****************************************************************************/ + +dng_opcode_list::dng_opcode_list (uint32 stage) + + : fList () + , fAlwaysApply (false) + , fStage (stage) + + { + + } + +/******************************************************************************/ + +dng_opcode_list::~dng_opcode_list () + { + + Clear (); + + } + +/******************************************************************************/ + +void dng_opcode_list::Clear () + { + + for (size_t index = 0; index < fList.size (); index++) + { + + if (fList [index]) + { + + delete fList [index]; + + fList [index] = NULL; + + } + + } + + fList.clear (); + + fAlwaysApply = false; + + } + +/******************************************************************************/ + +void dng_opcode_list::Swap (dng_opcode_list &otherList) + { + + fList.swap (otherList.fList); + + std::swap (fAlwaysApply, otherList.fAlwaysApply); + + std::swap (fStage, otherList.fStage); + + } + +/******************************************************************************/ + +uint32 dng_opcode_list::MinVersion (bool includeOptional) const + { + + uint32 result = dngVersion_None; + + for (size_t index = 0; index < fList.size (); index++) + { + + if (includeOptional || !fList [index]->Optional ()) + { + + result = Max_uint32 (result, fList [index]->MinVersion ()); + + } + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_opcode_list::Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image) + { + + for (uint32 index = 0; index < Count (); index++) + { + + dng_opcode &opcode (Entry (index)); + + if (opcode.AboutToApply (host, negative)) + { + + opcode.Apply (host, + negative, + image); + + } + + } + + } + +/*****************************************************************************/ + +void dng_opcode_list::Append (AutoPtr &opcode) + { + + if (opcode->OpcodeID () == dngOpcode_Private) + { + SetAlwaysApply (); + } + + opcode->SetStage (fStage); + + fList.push_back (NULL); + + fList [fList.size () - 1] = opcode.Release (); + + } + +/*****************************************************************************/ + +dng_memory_block * dng_opcode_list::Spool (dng_host &host) const + { + + if (IsEmpty ()) + { + return NULL; + } + + if (AlwaysApply ()) + { + ThrowProgramError (); + } + + dng_memory_stream stream (host.Allocator ()); + + stream.SetBigEndian (); + + stream.Put_uint32 ((uint32) fList.size ()); + + for (size_t index = 0; index < fList.size (); index++) + { + + stream.Put_uint32 (fList [index]->OpcodeID ()); + stream.Put_uint32 (fList [index]->MinVersion ()); + stream.Put_uint32 (fList [index]->Flags ()); + + fList [index]->PutData (stream); + + } + + return stream.AsMemoryBlock (host.Allocator ()); + + } + +/*****************************************************************************/ + +void dng_opcode_list::FingerprintToStream (dng_stream &stream) const + { + + if (IsEmpty ()) + { + return; + } + + stream.Put_uint32 ((uint32) fList.size ()); + + for (size_t index = 0; index < fList.size (); index++) + { + + stream.Put_uint32 (fList [index]->OpcodeID ()); + stream.Put_uint32 (fList [index]->MinVersion ()); + stream.Put_uint32 (fList [index]->Flags ()); + + if (fList [index]->OpcodeID () != dngOpcode_Private) + { + + fList [index]->PutData (stream); + + } + + } + + } + +/*****************************************************************************/ + +void dng_opcode_list::Parse (dng_host &host, + dng_stream &stream, + uint32 byteCount, + uint64 streamOffset) + { + + Clear (); + + TempBigEndian tempBigEndian (stream); + + stream.SetReadPosition (streamOffset); + + uint32 count = stream.Get_uint32 (); + + #if qDNGValidate + + if (gVerbose) + { + + if (count == 1) + { + printf ("1 opcode\n"); + } + + else + { + printf ("%u opcodes\n", (unsigned) count); + } + + } + + #endif + + for (uint32 index = 0; index < count; index++) + { + + uint32 opcodeID = stream.Get_uint32 (); + + AutoPtr opcode (host.Make_dng_opcode (opcodeID, + stream)); + + Append (opcode); + + } + + if (stream.Position () != streamOffset + byteCount) + { + + ThrowBadFormat ("Error parsing opcode list"); + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_opcode_list.h b/source/lib/dng_sdk/dng_opcode_list.h new file mode 100644 index 0000000..84813fb --- /dev/null +++ b/source/lib/dng_sdk/dng_opcode_list.h @@ -0,0 +1,165 @@ +/*****************************************************************************/ +// Copyright 2008-2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_opcode_list.h#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * List of opcodes. + */ + +/*****************************************************************************/ + +#ifndef __dng_opcode_list__ +#define __dng_opcode_list__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_opcodes.h" + +#include + +/*****************************************************************************/ + +/// A list of opcodes. + +class dng_opcode_list + { + + private: + + std::vector fList; + + bool fAlwaysApply; + + uint32 fStage; + + public: + + /// Create an empty opcode list for the specific image stage (1, 2, or 3). + + dng_opcode_list (uint32 stage); + + ~dng_opcode_list (); + + /// Is the opcode list empty? + + bool IsEmpty () const + { + return fList.size () == 0; + } + + /// Does the list contain at least 1 opcode? + + bool NotEmpty () const + { + return !IsEmpty (); + } + + /// Should the opcode list always be applied to the image? + + bool AlwaysApply () const + { + return fAlwaysApply && NotEmpty (); + } + + /// Set internal flag to indicate this opcode list should always be + /// applied. + + void SetAlwaysApply () + { + fAlwaysApply = true; + } + + /// The number of opcodes in this list. + + uint32 Count () const + { + return (uint32) fList.size (); + } + + /// Retrieve read/write opcode by index (must be in the range 0 to Count + /// () - 1). + + dng_opcode & Entry (uint32 index) + { + return *fList [index]; + } + + /// Retrieve read-only opcode by index (must be in the range 0 to Count + /// () - 1). + + const dng_opcode & Entry (uint32 index) const + { + return *fList [index]; + } + + /// Remove all opcodes from the list. + + void Clear (); + + /// Swap two opcode lists. + + void Swap (dng_opcode_list &otherList); + + /// Return minimum DNG version required to support all opcodes in this + /// list. If includeOptional is set to true, then this calculation will + /// include optional opcodes. + + uint32 MinVersion (bool includeOptional) const; + + /// Apply this opcode list to the specified image with corresponding + /// negative. + + void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + /// Append the specified opcode to this list. + + void Append (AutoPtr &opcode); + + /// Serialize this opcode list to a block of memory. The caller is + /// responsible for deleting this block. + + dng_memory_block * Spool (dng_host &host) const; + + /// Write a fingerprint of this opcode list to the specified stream. + + void FingerprintToStream (dng_stream &stream) const; + + /// Read an opcode list from the specified stream, starting at the + /// specified offset (streamOffset, in bytes). byteCount is provided for + /// error checking purposes. A bad format exception + /// will be thrown if the length of the opcode stream does not exactly + /// match byteCount. + + void Parse (dng_host &host, + dng_stream &stream, + uint32 byteCount, + uint64 streamOffset); + + private: + + // Hidden copy constructor and assignment operator. + + dng_opcode_list (const dng_opcode_list &list); + + dng_opcode_list & operator= (const dng_opcode_list &list); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_opcodes.cpp b/source/lib/dng_sdk/dng_opcodes.cpp new file mode 100644 index 0000000..dedf097 --- /dev/null +++ b/source/lib/dng_sdk/dng_opcodes.cpp @@ -0,0 +1,572 @@ +/*****************************************************************************/ +// Copyright 2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_opcodes.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_opcodes.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_filter_task.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_negative.h" +#include "dng_parse_utils.h" +#include "dng_stream.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +dng_opcode::dng_opcode (uint32 opcodeID, + uint32 minVersion, + uint32 flags) + + : fOpcodeID (opcodeID) + , fMinVersion (minVersion) + , fFlags (flags) + , fWasReadFromStream (false) + , fStage (0) + + { + + } + +/*****************************************************************************/ + +dng_opcode::dng_opcode (uint32 opcodeID, + dng_stream &stream, + const char *name) + + : fOpcodeID (opcodeID) + , fMinVersion (0) + , fFlags (0) + , fWasReadFromStream (true) + , fStage (0) + + { + + fMinVersion = stream.Get_uint32 (); + fFlags = stream.Get_uint32 (); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("\nOpcode: "); + + if (name) + { + printf ("%s", name); + } + else + { + printf ("Unknown (%u)", (unsigned) opcodeID); + } + + printf (", minVersion = %u.%u.%u.%u", + (unsigned) ((fMinVersion >> 24) & 0x0FF), + (unsigned) ((fMinVersion >> 16) & 0x0FF), + (unsigned) ((fMinVersion >> 8) & 0x0FF), + (unsigned) ((fMinVersion ) & 0x0FF)); + + printf (", flags = %u\n", (unsigned) fFlags); + + } + + #else + + (void) name; + + #endif + + } + +/*****************************************************************************/ + +dng_opcode::~dng_opcode () + { + + } + +/*****************************************************************************/ + +void dng_opcode::PutData (dng_stream &stream) const + { + + // No data by default + + stream.Put_uint32 (0); + + } + +/*****************************************************************************/ + +bool dng_opcode::AboutToApply (dng_host &host, + dng_negative &negative) + { + + if (SkipIfPreview () && host.ForPreview ()) + { + + negative.SetIsPreview (true); + + } + + else if (MinVersion () > dngVersion_Current && + WasReadFromStream ()) + { + + if (!Optional ()) + { + + // Somebody screwed up computing the DNGBackwardVersion... + + ThrowBadFormat (); + + } + + } + + else if (!IsValidForNegative (negative)) + { + + ThrowBadFormat (); + + } + + else if (!IsNOP ()) + { + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +dng_opcode_Unknown::dng_opcode_Unknown (dng_host &host, + uint32 opcodeID, + dng_stream &stream) + + : dng_opcode (opcodeID, + stream, + NULL) + + , fData () + + { + + uint32 size = stream.Get_uint32 (); + + if (size) + { + + fData.Reset (host.Allocate (size)); + + stream.Get (fData->Buffer (), + fData->LogicalSize ()); + + #if qDNGValidate + + if (gVerbose) + { + + DumpHexAscii (fData->Buffer_uint8 (), + fData->LogicalSize ()); + + } + + #endif + + } + + } + +/*****************************************************************************/ + +void dng_opcode_Unknown::PutData (dng_stream &stream) const + { + + if (fData.Get ()) + { + + stream.Put_uint32 (fData->LogicalSize ()); + + stream.Put (fData->Buffer (), + fData->LogicalSize ()); + + } + + else + { + + stream.Put_uint32 (0); + + } + + } + +/*****************************************************************************/ + +void dng_opcode_Unknown::Apply (dng_host & /* host */, + dng_negative & /* negative */, + AutoPtr & /* image */) + { + + // We should never need to apply an unknown opcode. + + if (!Optional ()) + { + + ThrowBadFormat (); + + } + + } + +/*****************************************************************************/ + +class dng_filter_opcode_task: public dng_filter_task + { + + private: + + dng_filter_opcode &fOpcode; + + dng_negative &fNegative; + + public: + + dng_filter_opcode_task (dng_filter_opcode &opcode, + dng_negative &negative, + const dng_image &srcImage, + dng_image &dstImage) + + : dng_filter_task (srcImage, + dstImage) + + , fOpcode (opcode) + , fNegative (negative) + + { + + fSrcPixelType = fOpcode.BufferPixelType (srcImage.PixelType ()); + + fDstPixelType = fSrcPixelType; + + fSrcRepeat = opcode.SrcRepeat (); + + } + + virtual dng_rect SrcArea (const dng_rect &dstArea) + { + + return fOpcode.SrcArea (dstArea, + fDstImage.Bounds ()); + + } + + virtual dng_point SrcTileSize (const dng_point &dstTileSize) + { + + return fOpcode.SrcTileSize (dstTileSize, + fDstImage.Bounds ()); + + } + + virtual void ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) + { + + fOpcode.ProcessArea (fNegative, + threadIndex, + srcBuffer, + dstBuffer, + dstBuffer.Area (), + fDstImage.Bounds ()); + + } + + virtual void Start (uint32 threadCount, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer) + { + + dng_filter_task::Start (threadCount, + tileSize, + allocator, + sniffer); + + fOpcode.Prepare (fNegative, + threadCount, + tileSize, + fDstImage.Bounds (), + fDstImage.Planes (), + fDstPixelType, + *allocator); + + } + + }; + +/*****************************************************************************/ + +dng_filter_opcode::dng_filter_opcode (uint32 opcodeID, + uint32 minVersion, + uint32 flags) + + : dng_opcode (opcodeID, + minVersion, + flags) + + { + + } + +/*****************************************************************************/ + +dng_filter_opcode::dng_filter_opcode (uint32 opcodeID, + dng_stream &stream, + const char *name) + + : dng_opcode (opcodeID, + stream, + name) + + { + + } + +/*****************************************************************************/ + +void dng_filter_opcode::Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image) + { + + dng_rect modifiedBounds = ModifiedBounds (image->Bounds ()); + + if (modifiedBounds.NotEmpty ()) + { + + // Allocate destination image. + + AutoPtr dstImage; + + // If we are processing the entire image, allocate an + // undefined image. + + if (modifiedBounds == image->Bounds ()) + { + + dstImage.Reset (host.Make_dng_image (image->Bounds (), + image->Planes (), + image->PixelType ())); + + } + + // Else start with a clone of the existing image. + + else + { + + dstImage.Reset (image->Clone ()); + + } + + // Filter the image. + + dng_filter_opcode_task task (*this, + negative, + *image, + *dstImage); + + host.PerformAreaTask (task, + modifiedBounds); + + // Return the new image. + + image.Reset (dstImage.Release ()); + + } + + } + +/*****************************************************************************/ + +class dng_inplace_opcode_task: public dng_area_task + { + + private: + + dng_inplace_opcode &fOpcode; + + dng_negative &fNegative; + + dng_image &fImage; + + uint32 fPixelType; + + AutoPtr fBuffer [kMaxMPThreads]; + + public: + + dng_inplace_opcode_task (dng_inplace_opcode &opcode, + dng_negative &negative, + dng_image &image) + + : dng_area_task () + + , fOpcode (opcode) + , fNegative (negative) + , fImage (image) + , fPixelType (opcode.BufferPixelType (image.PixelType ())) + + { + + } + + virtual void Start (uint32 threadCount, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer * /* sniffer */) + { + + uint32 pixelSize = TagTypeSize (fPixelType); + + uint32 bufferSize = tileSize.v * + RoundUpForPixelSize (tileSize.h, pixelSize) * + pixelSize * + fImage.Planes (); + + for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++) + { + + fBuffer [threadIndex] . Reset (allocator->Allocate (bufferSize)); + + } + + fOpcode.Prepare (fNegative, + threadCount, + tileSize, + fImage.Bounds (), + fImage.Planes (), + fPixelType, + *allocator); + + } + + virtual void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer * /* sniffer */) + { + + // Setup buffer. + + dng_pixel_buffer buffer; + + buffer.fArea = tile; + + buffer.fPlane = 0; + buffer.fPlanes = fImage.Planes (); + + buffer.fPixelType = fPixelType; + buffer.fPixelSize = TagTypeSize (fPixelType); + + buffer.fPlaneStep = RoundUpForPixelSize (tile.W (), + buffer.fPixelSize); + + buffer.fRowStep = buffer.fPlaneStep * + buffer.fPlanes; + + buffer.fData = fBuffer [threadIndex]->Buffer (); + + // Get source pixels. + + fImage.Get (buffer); + + // Process area. + + fOpcode.ProcessArea (fNegative, + threadIndex, + buffer, + tile, + fImage.Bounds ()); + + // Save result pixels. + + fImage.Put (buffer); + + } + + }; + +/*****************************************************************************/ + +dng_inplace_opcode::dng_inplace_opcode (uint32 opcodeID, + uint32 minVersion, + uint32 flags) + + : dng_opcode (opcodeID, + minVersion, + flags) + + { + + } + +/*****************************************************************************/ + +dng_inplace_opcode::dng_inplace_opcode (uint32 opcodeID, + dng_stream &stream, + const char *name) + + : dng_opcode (opcodeID, + stream, + name) + + { + + } + +/*****************************************************************************/ + +void dng_inplace_opcode::Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image) + { + + dng_rect modifiedBounds = ModifiedBounds (image->Bounds ()); + + if (modifiedBounds.NotEmpty ()) + { + + dng_inplace_opcode_task task (*this, + negative, + *image); + + host.PerformAreaTask (task, + modifiedBounds); + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_opcodes.h b/source/lib/dng_sdk/dng_opcodes.h new file mode 100644 index 0000000..b214540 --- /dev/null +++ b/source/lib/dng_sdk/dng_opcodes.h @@ -0,0 +1,507 @@ +/*****************************************************************************/ +// Copyright 2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_opcodes.h#2 $ */ +/* $DateTime: 2012/08/02 06:09:06 $ */ +/* $Change: 841096 $ */ +/* $Author: erichan $ */ + +/** \file + * Base class and common data structures for opcodes (introduced in DNG 1.3). + */ + +/*****************************************************************************/ + +#ifndef __dng_opcodes__ +#define __dng_opcodes__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_rect.h" +#include "dng_types.h" + +/*****************************************************************************/ + +/// \brief List of supported opcodes (by ID). + +enum dng_opcode_id + { + + // Internal use only opcode. Never written to DNGs. + + dngOpcode_Private = 0, + + // Warp image to correct distortion and lateral chromatic aberration for + // rectilinear lenses. + + dngOpcode_WarpRectilinear = 1, + + // Warp image to correction distortion for fisheye lenses (i.e., map the + // fisheye projection to a perspective projection). + + dngOpcode_WarpFisheye = 2, + + // Radial vignette correction. + + dngOpcode_FixVignetteRadial = 3, + + // Patch bad Bayer pixels which are marked with a special value in the image. + + dngOpcode_FixBadPixelsConstant = 4, + + // Patch bad Bayer pixels/rectangles at a list of specified coordinates. + + dngOpcode_FixBadPixelsList = 5, + + // Trim image to specified bounds. + + dngOpcode_TrimBounds = 6, + + // Map an area through a 16-bit LUT. + + dngOpcode_MapTable = 7, + + // Map an area using a polynomial function. + + dngOpcode_MapPolynomial = 8, + + // Apply a gain map to an area. + + dngOpcode_GainMap = 9, + + // Apply a per-row delta to an area. + + dngOpcode_DeltaPerRow = 10, + + // Apply a per-column delta to an area. + + dngOpcode_DeltaPerColumn = 11, + + // Apply a per-row scale to an area. + + dngOpcode_ScalePerRow = 12, + + // Apply a per-column scale to an area. + + dngOpcode_ScalePerColumn = 13 + + }; + +/*****************************************************************************/ + +/// \brief Virtual base class for opcode. + +class dng_opcode + { + + public: + + /// Opcode flags. + + enum + { + kFlag_None = 0, //!< No flag. + kFlag_Optional = 1, //!< This opcode is optional. + kFlag_SkipIfPreview = 2 //!< May skip opcode for preview images. + }; + + private: + + uint32 fOpcodeID; + + uint32 fMinVersion; + + uint32 fFlags; + + bool fWasReadFromStream; + + uint32 fStage; + + protected: + + dng_opcode (uint32 opcodeID, + uint32 minVersion, + uint32 flags); + + dng_opcode (uint32 opcodeID, + dng_stream &stream, + const char *name); + + public: + + virtual ~dng_opcode (); + + /// The ID of this opcode. + + uint32 OpcodeID () const + { + return fOpcodeID; + } + + /// The first DNG version that supports this opcode. + + uint32 MinVersion () const + { + return fMinVersion; + } + + /// The flags for this opcode. + + uint32 Flags () const + { + return fFlags; + } + + /// Is this opcode optional? + + bool Optional () const + { + return (Flags () & kFlag_Optional) != 0; + } + + /// Should the opcode be skipped when rendering preview images? + + bool SkipIfPreview () const + { + return (Flags () & kFlag_SkipIfPreview) != 0; + } + + /// Was this opcode read from a data stream? + + bool WasReadFromStream () const + { + return fWasReadFromStream; + } + + /// Which image processing stage (1, 2, 3) is associated with this + /// opcode? + + uint32 Stage () const + { + return fStage; + } + + /// Set the image processing stage (1, 2, 3) for this opcode. Stage 1 is + /// the original image data, including masked areas. Stage 2 is + /// linearized image data and trimmed to the active area. Stage 3 is + /// demosaiced and trimmed to the active area. + + void SetStage (uint32 stage) + { + fStage = stage; + } + + /// Is the opcode a NOP (i.e., does nothing)? An opcode could be a NOP + /// for some specific parameters. + + virtual bool IsNOP () const + { + return false; + } + + /// Is this opcode valid for the specified negative? + + virtual bool IsValidForNegative (const dng_negative & /* negative */) const + { + return true; + } + + /// Write opcode to a stream. + /// \param stream The stream to which to write the opcode data. + + virtual void PutData (dng_stream &stream) const; + + /// Perform error checking prior to applying this opcode to the + /// specified negative. Returns true if this opcode should be applied to + /// the negative, false otherwise. + + bool AboutToApply (dng_host &host, + dng_negative &negative); + + /// Apply this opcode to the specified image with associated negative. + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image) = 0; + + }; + +/*****************************************************************************/ + +/// \brief Class to represent unknown opcodes (e.g, opcodes defined in future +/// DNG versions). + +class dng_opcode_Unknown: public dng_opcode + { + + private: + + AutoPtr fData; + + public: + + dng_opcode_Unknown (dng_host &host, + uint32 opcodeID, + dng_stream &stream); + + virtual void PutData (dng_stream &stream) const; + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + }; + +/*****************************************************************************/ + +/// \brief Class to represent a filter opcode, such as a convolution. + +class dng_filter_opcode: public dng_opcode + { + + protected: + + dng_filter_opcode (uint32 opcodeID, + uint32 minVersion, + uint32 flags); + + dng_filter_opcode (uint32 opcodeID, + dng_stream &stream, + const char *name); + + public: + + /// The pixel data type of this opcode. + + virtual uint32 BufferPixelType (uint32 imagePixelType) + { + return imagePixelType; + } + + /// The adjusted bounds (processing area) of this opcode. It is limited to + /// the intersection of the specified image area and the GainMap area. + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds) + { + return imageBounds; + } + + /// Returns the width and height (in pixels) of the repeating mosaic pattern. + + virtual dng_point SrcRepeat () + { + return dng_point (1, 1); + } + + /// Returns the source pixel area needed to process a destination pixel area + /// that lies within the specified bounds. + /// \param dstArea The destination pixel area to be computed. + /// \param imageBounds The overall image area (dstArea will lie within these + /// bounds). + /// \retval The source pixel area needed to process the specified dstArea. + + virtual dng_rect SrcArea (const dng_rect &dstArea, + const dng_rect & /* imageBounds */) + { + return dstArea; + } + + /// Given a destination tile size, calculate input tile size. Simlar to + /// SrcArea, and should seldom be overridden. + /// + /// \param dstTileSize The destination tile size that is targeted for output. + /// + /// \param imageBounds The image bounds (the destination tile will + /// always lie within these bounds). + /// + /// \retval The source tile size needed to compute a tile of the destination + /// size. + + virtual dng_point SrcTileSize (const dng_point &dstTileSize, + const dng_rect &imageBounds) + { + return SrcArea (dng_rect (dstTileSize), + imageBounds).Size (); + } + + /// Startup method called before any processing is performed on pixel areas. + /// It can be used to allocate (per-thread) memory and setup tasks. + /// + /// \param negative The negative object to be processed. + /// + /// \param threadCount The number of threads to be used to perform the + /// processing. + /// + /// \param threadCount Total number of threads that will be used for + /// processing. Less than or equal to MaxThreads. + /// + /// \param tileSize Size of source tiles which will be processed. (Not all + /// tiles will be this size due to edge conditions.) + /// + /// \param imageBounds Total size of image to be processed. + /// + /// \param imagePlanes Number of planes in the image. Less than or equal to + /// kMaxColorPlanes. + /// + /// \param bufferPixelType Pixel type of image buffer (see dng_tag_types.h). + /// + /// \param allocator dng_memory_allocator to use for allocating temporary + /// buffers, etc. + + virtual void Prepare (dng_negative & /* negative */, + uint32 /* threadCount */, + const dng_point & /* tileSize */, + const dng_rect & /* imageBounds */, + uint32 /* imagePlanes */, + uint32 /* bufferPixelType */, + dng_memory_allocator & /* allocator */) + { + } + + /// Implements filtering operation from one buffer to another. Source + /// and destination pixels are set up in member fields of this class. + /// Ideally, no allocation should be done in this routine. + /// + /// \param negative The negative associated with the pixels to be + /// processed. + /// + /// \param threadIndex The thread on which this routine is being called, + /// between 0 and threadCount - 1 for the threadCount passed to Prepare + /// method. + /// + /// \param srcBuffer Input area and source pixels. + /// + /// \param dstBuffer Destination pixels. + /// + /// \param dstArea Destination pixel processing area. + /// + /// \param imageBounds Total image area to be processed; dstArea will + /// always lie within these bounds. + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer, + const dng_rect &dstArea, + const dng_rect &imageBounds) = 0; + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + }; + +/*****************************************************************************/ + +/// \brief Class to represent an in-place (i.e., pointwise, per-pixel) opcode, +/// such as a global tone curve. + +class dng_inplace_opcode: public dng_opcode + { + + protected: + + dng_inplace_opcode (uint32 opcodeID, + uint32 minVersion, + uint32 flags); + + dng_inplace_opcode (uint32 opcodeID, + dng_stream &stream, + const char *name); + + public: + + /// The pixel data type of this opcode. + + virtual uint32 BufferPixelType (uint32 imagePixelType) + { + return imagePixelType; + } + + /// The adjusted bounds (processing area) of this opcode. It is limited to + /// the intersection of the specified image area and the GainMap area. + + virtual dng_rect ModifiedBounds (const dng_rect &imageBounds) + { + return imageBounds; + } + + /// Startup method called before any processing is performed on pixel areas. + /// It can be used to allocate (per-thread) memory and setup tasks. + /// + /// \param negative The negative object to be processed. + /// + /// \param threadCount The number of threads to be used to perform the + /// processing. + /// + /// \param threadCount Total number of threads that will be used for + /// processing. Less than or equal to MaxThreads. + /// + /// \param tileSize Size of source tiles which will be processed. (Not all + /// tiles will be this size due to edge conditions.) + /// + /// \param imageBounds Total size of image to be processed. + /// + /// \param imagePlanes Number of planes in the image. Less than or equal to + /// kMaxColorPlanes. + /// + /// \param bufferPixelType Pixel type of image buffer (see dng_tag_types.h). + /// + /// \param allocator dng_memory_allocator to use for allocating temporary + /// buffers, etc. + + virtual void Prepare (dng_negative & /* negative */, + uint32 /* threadCount */, + const dng_point & /* tileSize */, + const dng_rect & /* imageBounds */, + uint32 /* imagePlanes */, + uint32 /* bufferPixelType */, + dng_memory_allocator & /* allocator */) + { + } + + /// Implements image processing operation in a single buffer. The source + /// pixels are provided as input to the buffer, and this routine + /// calculates and writes the destination pixels to the same buffer. + /// Ideally, no allocation should be done in this routine. + /// + /// \param negative The negative associated with the pixels to be + /// processed. + /// + /// \param threadIndex The thread on which this routine is being called, + /// between 0 and threadCount - 1 for the threadCount passed to Prepare + /// method. + /// + /// \param srcBuffer Input area and source pixels. + /// + /// \param dstBuffer Destination pixels. + /// + /// \param dstArea Destination pixel processing area. + /// + /// \param imageBounds Total image area to be processed; dstArea will + /// always lie within these bounds. + + virtual void ProcessArea (dng_negative &negative, + uint32 threadIndex, + dng_pixel_buffer &buffer, + const dng_rect &dstArea, + const dng_rect &imageBounds) = 0; + + virtual void Apply (dng_host &host, + dng_negative &negative, + AutoPtr &image); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_orientation.cpp b/source/lib/dng_sdk/dng_orientation.cpp new file mode 100644 index 0000000..820ca34 --- /dev/null +++ b/source/lib/dng_sdk/dng_orientation.cpp @@ -0,0 +1,233 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_orientation.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +#include "dng_orientation.h" + +/*****************************************************************************/ + +void dng_orientation::SetTIFF (uint32 tiff) + { + + switch (tiff) + { + + case 1: + { + fAdobeOrientation = kNormal; + break; + } + + case 2: + { + fAdobeOrientation = kMirror; + break; + } + + case 3: + { + fAdobeOrientation = kRotate180; + break; + } + + case 4: + { + fAdobeOrientation = kMirror180; + break; + } + + case 5: + { + fAdobeOrientation = kMirror90CCW; + break; + } + + case 6: + { + fAdobeOrientation = kRotate90CW; + break; + } + + case 7: + { + fAdobeOrientation = kMirror90CW; + break; + } + + case 8: + { + fAdobeOrientation = kRotate90CCW; + break; + } + + case 9: + { + fAdobeOrientation = kUnknown; + break; + } + + default: + { + fAdobeOrientation = kNormal; + } + + } + + } + +/*****************************************************************************/ + +uint32 dng_orientation::GetTIFF () const + { + + switch (fAdobeOrientation) + { + + case kNormal: + { + return 1; + } + + case kMirror: + { + return 2; + } + + case kRotate180: + { + return 3; + } + + case kMirror180: + { + return 4; + } + + case kMirror90CCW: + { + return 5; + } + + case kRotate90CW: + { + return 6; + } + + case kMirror90CW: + { + return 7; + } + + case kRotate90CCW: + { + return 8; + } + + case kUnknown: + { + return 9; + } + + default: + break; + + } + + return 1; + + } + +/*****************************************************************************/ + +bool dng_orientation::FlipD () const + { + + return (fAdobeOrientation & 1) != 0; + + } + +/*****************************************************************************/ + +bool dng_orientation::FlipH () const + { + + if (fAdobeOrientation & 4) + return (fAdobeOrientation & 2) == 0; + + else + return (fAdobeOrientation & 2) != 0; + + } + +/*****************************************************************************/ + +bool dng_orientation::FlipV () const + { + + if (fAdobeOrientation & 4) + return FlipD () == FlipH (); + + else + return FlipD () != FlipH (); + + } + +/*****************************************************************************/ + +dng_orientation dng_orientation::operator- () const + { + + uint32 x = GetAdobe (); + + if ((x & 5) == 5) + { + + x ^= 2; + + } + + dng_orientation result; + + result.SetAdobe (((4 - x) & 3) | (x & 4)); + + return result; + + } + +/*****************************************************************************/ + +dng_orientation dng_orientation::operator+ (const dng_orientation &b) const + { + + uint32 x = GetAdobe (); + + uint32 y = b.GetAdobe (); + + if (y & 4) + { + + if (x & 1) + x ^= 6; + else + x ^= 4; + + } + + dng_orientation result; + + result.SetAdobe (((x + y) & 3) | (x & 4)); + + return result; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_orientation.h b/source/lib/dng_sdk/dng_orientation.h new file mode 100644 index 0000000..cb23adf --- /dev/null +++ b/source/lib/dng_sdk/dng_orientation.h @@ -0,0 +1,189 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_orientation.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/******************************************************************************/ + +#ifndef __dng_orientation__ +#define __dng_orientation__ + +/******************************************************************************/ + +#include "dng_types.h" + +/******************************************************************************/ + +class dng_orientation + { + + private: + + // We internally use an orientation encoding ("Adobe") that is + // different than the TIFF orientation encoding ("TIFF"). + + uint32 fAdobeOrientation; + + public: + enum + { + kNormal = 0, + kRotate90CW = 1, + kRotate180 = 2, + kRotate90CCW = 3, + kMirror = 4, + kMirror90CW = 5, + kMirror180 = 6, + kMirror90CCW = 7, + kUnknown = 8 + }; + + + dng_orientation () + + : fAdobeOrientation (kNormal) + + { + } + + void SetAdobe (uint32 adobe) + { + fAdobeOrientation = adobe; + } + + uint32 GetAdobe () const + { + return fAdobeOrientation; + } + + void SetTIFF (uint32 tiff); + + uint32 GetTIFF () const; + + static dng_orientation AdobeToDNG (uint32 adobe) + { + + dng_orientation result; + + result.SetAdobe (adobe); + + return result; + + } + + static dng_orientation TIFFtoDNG (uint32 tiff) + { + + dng_orientation result; + + result.SetTIFF (tiff); + + return result; + + } + + static dng_orientation Normal () + { + return AdobeToDNG (kNormal); + } + + static dng_orientation Rotate90CW () + { + return AdobeToDNG (kRotate90CW); + } + + static dng_orientation Rotate180 () + { + return AdobeToDNG (kRotate180); + } + + static dng_orientation Rotate90CCW () + { + return AdobeToDNG (kRotate90CCW); + } + + static dng_orientation Mirror () + { + return AdobeToDNG (kMirror); + } + + static dng_orientation Mirror90CW () + { + return AdobeToDNG (kMirror90CW); + } + + static dng_orientation Mirror180 () + { + return AdobeToDNG (kMirror180); + } + + static dng_orientation Mirror90CCW () + { + return AdobeToDNG (kMirror90CCW); + } + + static dng_orientation Unknown () + { + return AdobeToDNG (kUnknown); + } + + bool IsValid () const + { + return fAdobeOrientation < kUnknown; + } + + bool NotValid () const + { + return !IsValid (); + } + + bool FlipD () const; + + bool FlipH () const; + + bool FlipV () const; + + bool operator== (const dng_orientation &b) const + { + return fAdobeOrientation == b.fAdobeOrientation; + } + + bool operator!= (const dng_orientation &b) const + { + return !(*this == b); + } + + dng_orientation operator- () const; + + dng_orientation operator+ (const dng_orientation &b) const; + + dng_orientation operator- (const dng_orientation &b) const + { + return (*this) + (-b); + } + + void operator+= (const dng_orientation &b) + { + *this = *this + b; + } + + void operator-= (const dng_orientation &b) + { + *this = *this - b; + } + + }; + +/******************************************************************************/ + +#endif + +/******************************************************************************/ diff --git a/source/lib/dng_sdk/dng_parse_utils.cpp b/source/lib/dng_sdk/dng_parse_utils.cpp new file mode 100644 index 0000000..879efbc --- /dev/null +++ b/source/lib/dng_sdk/dng_parse_utils.cpp @@ -0,0 +1,3326 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_parse_utils.cpp#3 $ */ +/* $DateTime: 2012/06/06 12:08:58 $ */ +/* $Change: 833617 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_parse_utils.h" + +#include "dng_date_time.h" +#include "dng_globals.h" +#include "dng_ifd.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_types.h" +#include "dng_stream.h" +#include "dng_exceptions.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +#if qDNGValidate + +/*****************************************************************************/ + +struct dng_name_table + { + uint32 key; + const char *name; + }; + +/*****************************************************************************/ + +static const char * LookupName (uint32 key, + const dng_name_table *table, + uint32 table_entries) + { + + for (uint32 index = 0; index < table_entries; index++) + { + + if (key == table [index] . key) + { + + return table [index] . name; + + } + + } + + return NULL; + + } + +/*****************************************************************************/ + +const char * LookupParentCode (uint32 parentCode) + { + + const dng_name_table kParentCodeNames [] = + { + { 0, "IFD 0" }, + { tcExifIFD, "Exif IFD" }, + { tcGPSInfo, "GPS IFD" }, + { tcInteroperabilityIFD, "Interoperability IFD" }, + { tcKodakDCRPrivateIFD, "Kodak DCR Private IFD" }, + { tcKodakKDCPrivateIFD, "Kodak KDC Private IFD" }, + { tcCanonMakerNote, "Canon MakerNote" }, + { tcEpsonMakerNote, "Epson MakerNote" }, + { tcFujiMakerNote, "Fuji MakerNote" }, + { tcHasselbladMakerNote, "Hasselblad MakerNote" }, + { tcKodakMakerNote, "Kodak MakerNote" }, + { tcKodakMakerNote65280, "Kodak MakerNote 65280" }, + { tcLeicaMakerNote, "Leica MakerNote" }, + { tcMamiyaMakerNote, "Mamiya MakerNote" }, + { tcMinoltaMakerNote, "Minolta MakerNote" }, + { tcNikonMakerNote, "Nikon MakerNote" }, + { tcOlympusMakerNote, "Olympus MakerNote" }, + { tcOlympusMakerNote8208, "Olympus MakerNote 8208" }, + { tcOlympusMakerNote8224, "Olympus MakerNote 8224" }, + { tcOlympusMakerNote8240, "Olympus MakerNote 8240" }, + { tcOlympusMakerNote8256, "Olympus MakerNote 8256" }, + { tcOlympusMakerNote8272, "Olympus MakerNote 8272" }, + { tcOlympusMakerNote12288, "Olympus MakerNote 12288" }, + { tcPanasonicMakerNote, "Panasonic MakerNote" }, + { tcPentaxMakerNote, "Pentax MakerNote" }, + { tcPhaseOneMakerNote, "Phase One MakerNote" }, + { tcRicohMakerNote, "Ricoh MakerNote" }, + { tcRicohMakerNoteCameraInfo, "Ricoh MakerNote Camera Info" }, + { tcSonyMakerNote, "Sony MakerNote" }, + { tcSonyMakerNoteSubInfo, "Sony MakerNote SubInfo" }, + { tcSonyPrivateIFD1, "Sony Private IFD 1" }, + { tcSonyPrivateIFD2, "Sony Private IFD 2" }, + { tcSonyPrivateIFD3A, "Sony Private IFD 3A" }, + { tcSonyPrivateIFD3B, "Sony Private IFD 3B" }, + { tcSonyPrivateIFD3C, "Sony Private IFD 3C" }, + { tcCanonCRW, "Canon CRW" }, + { tcContaxRAW, "Contax RAW" }, + { tcFujiRAF, "Fuji RAF" }, + { tcLeafMOS, "Leaf MOS" }, + { tcMinoltaMRW, "Minolta MRW" }, + { tcPanasonicRAW, "Panasonic RAW" }, + { tcFoveonX3F, "Foveon X3F" }, + { tcJPEG, "JPEG" }, + { tcAdobePSD, "Adobe PSD" } + }; + + const char *name = LookupName (parentCode, + kParentCodeNames, + sizeof (kParentCodeNames ) / + sizeof (kParentCodeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + if (parentCode >= tcFirstSubIFD && + parentCode <= tcLastSubIFD) + { + + sprintf (s, "SubIFD %u", (unsigned) (parentCode - tcFirstSubIFD + 1)); + + } + + else if (parentCode >= tcFirstChainedIFD && + parentCode <= tcLastChainedIFD) + { + + sprintf (s, "Chained IFD %u", (unsigned) (parentCode - tcFirstChainedIFD + 1)); + + } + + else + { + + sprintf (s, "ParentIFD %u", (unsigned) parentCode); + + } + + return s; + + } + +/*****************************************************************************/ + +const char * LookupTagCode (uint32 parentCode, + uint32 tagCode) + { + + const dng_name_table kTagNames [] = + { + { tcNewSubFileType, "NewSubFileType" }, + { tcSubFileType, "SubFileType" }, + { tcImageWidth, "ImageWidth" }, + { tcImageLength, "ImageLength" }, + { tcBitsPerSample, "BitsPerSample" }, + { tcCompression, "Compression" }, + { tcPhotometricInterpretation, "PhotometricInterpretation" }, + { tcThresholding, "Thresholding" }, + { tcCellWidth, "CellWidth" }, + { tcCellLength, "CellLength" }, + { tcFillOrder, "FillOrder" }, + { tcImageDescription, "ImageDescription" }, + { tcMake, "Make" }, + { tcModel, "Model" }, + { tcStripOffsets, "StripOffsets" }, + { tcOrientation, "Orientation" }, + { tcSamplesPerPixel, "SamplesPerPixel" }, + { tcRowsPerStrip, "RowsPerStrip" }, + { tcStripByteCounts, "StripByteCounts" }, + { tcMinSampleValue, "MinSampleValue" }, + { tcMaxSampleValue, "MaxSampleValue" }, + { tcXResolution, "XResolution" }, + { tcYResolution, "YResolution" }, + { tcPlanarConfiguration, "PlanarConfiguration" }, + { tcFreeOffsets, "FreeOffsets" }, + { tcFreeByteCounts, "FreeByteCounts" }, + { tcGrayResponseUnit, "GrayResponseUnit" }, + { tcGrayResponseCurve, "GrayResponseCurve" }, + { tcResolutionUnit, "ResolutionUnit" }, + { tcTransferFunction, "TransferFunction" }, + { tcSoftware, "Software" }, + { tcDateTime, "DateTime" }, + { tcArtist, "Artist" }, + { tcHostComputer, "HostComputer" }, + { tcWhitePoint, "WhitePoint" }, + { tcPrimaryChromaticities, "PrimaryChromaticities" }, + { tcColorMap, "ColorMap" }, + { tcTileWidth, "TileWidth" }, + { tcTileLength, "TileLength" }, + { tcTileOffsets, "TileOffsets" }, + { tcTileByteCounts, "TileByteCounts" }, + { tcSubIFDs, "SubIFDs" }, + { tcExtraSamples, "ExtraSamples" }, + { tcSampleFormat, "SampleFormat" }, + { tcJPEGTables, "JPEGTables" }, + { tcJPEGProc, "JPEGProc" }, + { tcJPEGInterchangeFormat, "JPEGInterchangeFormat" }, + { tcJPEGInterchangeFormatLength, "JPEGInterchangeFormatLength" }, + { tcYCbCrCoefficients, "YCbCrCoefficients" }, + { tcYCbCrSubSampling, "YCbCrSubSampling" }, + { tcYCbCrPositioning, "YCbCrPositioning" }, + { tcReferenceBlackWhite, "ReferenceBlackWhite" }, + { tcXMP, "XMP" }, + { tcKodakCameraSerialNumber, "KodakCameraSerialNumber" }, + { tcCFARepeatPatternDim, "CFARepeatPatternDim" }, + { tcCFAPattern, "CFAPattern" }, + { tcBatteryLevel, "BatteryLevel" }, + { tcKodakDCRPrivateIFD, "KodakDCRPrivateIFD" }, + { tcCopyright, "Copyright" }, + { tcExposureTime, "ExposureTime" }, + { tcFNumber, "FNumber" }, + { tcIPTC_NAA, "IPTC/NAA" }, + { tcLeafPKTS, "LeafPKTS" }, + { tcAdobeData, "AdobeData" }, + { tcExifIFD, "ExifIFD" }, + { tcICCProfile, "ICCProfile" }, + { tcExposureProgram, "ExposureProgram" }, + { tcSpectralSensitivity, "SpectralSensitivity" }, + { tcGPSInfo, "GPSInfo" }, + { tcISOSpeedRatings, "ISOSpeedRatings" }, + { tcOECF, "OECF" }, + { tcInterlace, "Interlace" }, + { tcTimeZoneOffset, "TimeZoneOffset" }, + { tcSelfTimerMode, "SelfTimerMode" }, + { tcSensitivityType, "SensitivityType" }, + { tcStandardOutputSensitivity, "StandardOutputSensitivity" }, + { tcRecommendedExposureIndex, "RecommendedExposureIndex" }, + { tcISOSpeed, "ISOSpeed" }, + { tcISOSpeedLatitudeyyy, "ISOSpeedLatitudeyyy" }, + { tcISOSpeedLatitudezzz, "ISOSpeedLatitudezzz" }, + { tcExifVersion, "ExifVersion" }, + { tcDateTimeOriginal, "DateTimeOriginal" }, + { tcDateTimeDigitized, "DateTimeDigitized" }, + { tcComponentsConfiguration, "ComponentsConfiguration" }, + { tcCompressedBitsPerPixel, "CompressedBitsPerPixel" }, + { tcShutterSpeedValue, "ShutterSpeedValue" }, + { tcApertureValue, "ApertureValue" }, + { tcBrightnessValue, "BrightnessValue" }, + { tcExposureBiasValue, "ExposureBiasValue" }, + { tcMaxApertureValue, "MaxApertureValue" }, + { tcSubjectDistance, "SubjectDistance" }, + { tcMeteringMode, "MeteringMode" }, + { tcLightSource, "LightSource" }, + { tcFlash, "Flash" }, + { tcFocalLength, "FocalLength" }, + { tcFlashEnergy, "FlashEnergy" }, + { tcSpatialFrequencyResponse, "SpatialFrequencyResponse" }, + { tcNoise, "Noise" }, + { tcFocalPlaneXResolution, "FocalPlaneXResolution" }, + { tcFocalPlaneYResolution, "FocalPlaneYResolution" }, + { tcFocalPlaneResolutionUnit, "FocalPlaneResolutionUnit" }, + { tcImageNumber, "ImageNumber" }, + { tcSecurityClassification, "SecurityClassification" }, + { tcImageHistory, "ImageHistory" }, + { tcSubjectArea, "SubjectArea" }, + { tcExposureIndex, "ExposureIndex" }, + { tcTIFF_EP_StandardID, "TIFF/EPStandardID" }, + { tcSensingMethod, "SensingMethod" }, + { tcMakerNote, "MakerNote" }, + { tcUserComment, "UserComment" }, + { tcSubsecTime, "SubsecTime" }, + { tcSubsecTimeOriginal, "SubsecTimeOriginal" }, + { tcSubsecTimeDigitized, "SubsecTimeDigitized" }, + { tcAdobeLayerData, "AdobeLayerData" }, + { tcFlashPixVersion, "FlashPixVersion" }, + { tcColorSpace, "ColorSpace" }, + { tcPixelXDimension, "PixelXDimension" }, + { tcPixelYDimension, "PixelYDimension" }, + { tcRelatedSoundFile, "RelatedSoundFile" }, + { tcInteroperabilityIFD, "InteroperabilityIFD" }, + { tcFlashEnergyExif, "FlashEnergyExif" }, + { tcSpatialFrequencyResponseExif, "SpatialFrequencyResponseExif" }, + { tcFocalPlaneXResolutionExif, "FocalPlaneXResolutionExif" }, + { tcFocalPlaneYResolutionExif, "FocalPlaneYResolutionExif" }, + { tcFocalPlaneResolutionUnitExif, "FocalPlaneResolutionUnitExif" }, + { tcSubjectLocation, "SubjectLocation" }, + { tcExposureIndexExif, "ExposureIndexExif" }, + { tcSensingMethodExif, "SensingMethodExif" }, + { tcFileSource, "FileSource" }, + { tcSceneType, "SceneType" }, + { tcCFAPatternExif, "CFAPatternExif" }, + { tcCustomRendered, "CustomRendered" }, + { tcExposureMode, "ExposureMode" }, + { tcWhiteBalance, "WhiteBalance" }, + { tcDigitalZoomRatio, "DigitalZoomRatio" }, + { tcFocalLengthIn35mmFilm, "FocalLengthIn35mmFilm" }, + { tcSceneCaptureType, "SceneCaptureType" }, + { tcGainControl, "GainControl" }, + { tcContrast, "Contrast" }, + { tcSaturation, "Saturation" }, + { tcSharpness, "Sharpness" }, + { tcDeviceSettingDescription, "DeviceSettingDescription" }, + { tcSubjectDistanceRange, "SubjectDistanceRange" }, + { tcImageUniqueID, "ImageUniqueID" }, + { tcCameraOwnerNameExif, "CameraOwnerNameExif" }, + { tcCameraSerialNumberExif, "CameraSerialNumberExif" }, + { tcLensSpecificationExif, "LensSpecificationExif" }, + { tcLensMakeExif, "LensMakeExif" }, + { tcLensModelExif, "LensModelExif" }, + { tcLensSerialNumberExif, "LensSerialNumberExif" }, + { tcGamma, "Gamma" }, + { tcPrintImageMatchingInfo, "PrintImageMatchingInfo" }, + { tcDNGVersion, "DNGVersion" }, + { tcDNGBackwardVersion, "DNGBackwardVersion" }, + { tcUniqueCameraModel, "UniqueCameraModel" }, + { tcLocalizedCameraModel, "LocalizedCameraModel" }, + { tcCFAPlaneColor, "CFAPlaneColor" }, + { tcCFALayout, "CFALayout" }, + { tcLinearizationTable, "LinearizationTable" }, + { tcBlackLevelRepeatDim, "BlackLevelRepeatDim" }, + { tcBlackLevel, "BlackLevel" }, + { tcBlackLevelDeltaH, "BlackLevelDeltaH" }, + { tcBlackLevelDeltaV, "BlackLevelDeltaV" }, + { tcWhiteLevel, "WhiteLevel" }, + { tcDefaultScale, "DefaultScale" }, + { tcDefaultCropOrigin, "DefaultCropOrigin" }, + { tcDefaultCropSize, "DefaultCropSize" }, + { tcDefaultUserCrop, "DefaultUserCrop" }, + { tcColorMatrix1, "ColorMatrix1" }, + { tcColorMatrix2, "ColorMatrix2" }, + { tcCameraCalibration1, "CameraCalibration1" }, + { tcCameraCalibration2, "CameraCalibration2" }, + { tcReductionMatrix1, "ReductionMatrix1" }, + { tcReductionMatrix2, "ReductionMatrix2" }, + { tcAnalogBalance, "AnalogBalance" }, + { tcAsShotNeutral, "AsShotNeutral" }, + { tcAsShotWhiteXY, "AsShotWhiteXY" }, + { tcBaselineExposure, "BaselineExposure" }, + { tcBaselineNoise, "BaselineNoise" }, + { tcBaselineSharpness, "BaselineSharpness" }, + { tcBayerGreenSplit, "BayerGreenSplit" }, + { tcLinearResponseLimit, "LinearResponseLimit" }, + { tcCameraSerialNumber, "CameraSerialNumber" }, + { tcLensInfo, "LensInfo" }, + { tcChromaBlurRadius, "ChromaBlurRadius" }, + { tcAntiAliasStrength, "AntiAliasStrength" }, + { tcShadowScale, "ShadowScale" }, + { tcDNGPrivateData, "DNGPrivateData" }, + { tcMakerNoteSafety, "MakerNoteSafety" }, + { tcCalibrationIlluminant1, "CalibrationIlluminant1" }, + { tcCalibrationIlluminant2, "CalibrationIlluminant2" }, + { tcBestQualityScale, "BestQualityScale" }, + { tcRawDataUniqueID, "RawDataUniqueID" }, + { tcOriginalRawFileName, "OriginalRawFileName" }, + { tcOriginalRawFileData, "OriginalRawFileData" }, + { tcActiveArea, "ActiveArea" }, + { tcMaskedAreas, "MaskedAreas" }, + { tcAsShotICCProfile, "AsShotICCProfile" }, + { tcAsShotPreProfileMatrix, "AsShotPreProfileMatrix" }, + { tcCurrentICCProfile, "CurrentICCProfile" }, + { tcCurrentPreProfileMatrix, "CurrentPreProfileMatrix" }, + { tcColorimetricReference, "ColorimetricReference" }, + { tcCameraCalibrationSignature, "CameraCalibrationSignature" }, + { tcProfileCalibrationSignature, "ProfileCalibrationSignature" }, + { tcExtraCameraProfiles, "ExtraCameraProfiles" }, + { tcAsShotProfileName, "AsShotProfileName" }, + { tcNoiseReductionApplied, "NoiseReductionApplied" }, + { tcProfileName, "ProfileName" }, + { tcProfileHueSatMapDims, "ProfileHueSatMapDims" }, + { tcProfileHueSatMapData1, "ProfileHueSatMapData1" }, + { tcProfileHueSatMapData2, "ProfileHueSatMapData2" }, + { tcProfileHueSatMapEncoding, "ProfileHueSatMapEncoding" }, + { tcProfileToneCurve, "ProfileToneCurve" }, + { tcProfileEmbedPolicy, "ProfileEmbedPolicy" }, + { tcProfileCopyright, "ProfileCopyright" }, + { tcForwardMatrix1, "ForwardMatrix1" }, + { tcForwardMatrix2, "ForwardMatrix2" }, + { tcPreviewApplicationName, "PreviewApplicationName" }, + { tcPreviewApplicationVersion, "PreviewApplicationVersion" }, + { tcPreviewSettingsName, "PreviewSettingsName" }, + { tcPreviewSettingsDigest, "PreviewSettingsDigest" }, + { tcPreviewColorSpace, "PreviewColorSpace" }, + { tcPreviewDateTime, "PreviewDateTime" }, + { tcRawImageDigest, "RawImageDigest" }, + { tcOriginalRawFileDigest, "OriginalRawFileDigest" }, + { tcSubTileBlockSize, "SubTileBlockSize" }, + { tcRowInterleaveFactor, "RowInterleaveFactor" }, + { tcProfileLookTableDims, "ProfileLookTableDims" }, + { tcProfileLookTableData, "ProfileLookTableData" }, + { tcProfileLookTableEncoding, "ProfileLookTableEncoding" }, + { tcBaselineExposureOffset, "BaselineExposureOffset" }, + { tcDefaultBlackRender, "DefaultBlackRender" }, + { tcOpcodeList1, "OpcodeList1" }, + { tcOpcodeList2, "OpcodeList2" }, + { tcOpcodeList3, "OpcodeList3" }, + { tcNoiseProfile, "NoiseProfile" }, + { tcOriginalDefaultFinalSize, "OriginalDefaultFinalSize" }, + { tcOriginalBestQualityFinalSize, "OriginalBestQualityFinalSize" }, + { tcOriginalDefaultCropSize, "OriginalDefaultCropSize" }, + { tcProfileHueSatMapEncoding, "ProfileHueSatMapEncoding" }, + { tcProfileLookTableEncoding, "ProfileLookTableEncoding" }, + { tcBaselineExposureOffset, "BaselineExposureOffset" }, + { tcDefaultBlackRender, "DefaultBlackRender" }, + { tcNewRawImageDigest, "NewRawImageDigest" }, + { tcRawToPreviewGain, "RawToPreviewGain" }, + { tcCacheBlob, "CacheBlob" }, + { tcKodakKDCPrivateIFD, "KodakKDCPrivateIFD" } + }; + + const dng_name_table kGPSTagNames [] = + { + { tcGPSVersionID, "GPSVersionID" }, + { tcGPSLatitudeRef, "GPSLatitudeRef" }, + { tcGPSLatitude, "GPSLatitude" }, + { tcGPSLongitudeRef, "GPSLongitudeRef" }, + { tcGPSLongitude, "GPSLongitude" }, + { tcGPSAltitudeRef, "GPSAltitudeRef" }, + { tcGPSAltitude, "GPSAltitude" }, + { tcGPSTimeStamp, "GPSTimeStamp" }, + { tcGPSSatellites, "GPSSatellites" }, + { tcGPSStatus, "GPSStatus" }, + { tcGPSMeasureMode, "GPSMeasureMode" }, + { tcGPSDOP, "GPSDOP" }, + { tcGPSSpeedRef, "GPSSpeedRef" }, + { tcGPSSpeed, "GPSSpeed" }, + { tcGPSTrackRef, "GPSTrackRef" }, + { tcGPSTrack, "GPSTrack" }, + { tcGPSImgDirectionRef, "GPSImgDirectionRef" }, + { tcGPSImgDirection, "GPSImgDirection" }, + { tcGPSMapDatum, "GPSMapDatum" }, + { tcGPSDestLatitudeRef, "GPSDestLatitudeRef" }, + { tcGPSDestLatitude, "GPSDestLatitude" }, + { tcGPSDestLongitudeRef, "GPSDestLongitudeRef" }, + { tcGPSDestLongitude, "GPSDestLongitude" }, + { tcGPSDestBearingRef, "GPSDestBearingRef" }, + { tcGPSDestBearing, "GPSDestBearing" }, + { tcGPSDestDistanceRef, "GPSDestDistanceRef" }, + { tcGPSDestDistance, "GPSDestDistance" }, + { tcGPSProcessingMethod, "GPSProcessingMethod" }, + { tcGPSAreaInformation, "GPSAreaInformation" }, + { tcGPSDateStamp, "GPSDateStamp" }, + { tcGPSDifferential, "GPSDifferential" }, + { tcGPSHPositioningError, "GPSHPositioningError" }, + }; + + const dng_name_table kInteroperabilityTagNames [] = + { + { tcInteroperabilityIndex, "InteroperabilityIndex" }, + { tcInteroperabilityVersion, "InteroperabilityVersion" }, + { tcRelatedImageFileFormat, "RelatedImageFileFormat" }, + { tcRelatedImageWidth, "RelatedImageWidth" }, + { tcRelatedImageLength, "RelatedImageLength" } + }; + + const dng_name_table kFujiTagNames [] = + { + { tcFujiHeader, "FujiHeader" }, + { tcFujiRawInfo1, "FujiRawInfo1" }, + { tcFujiRawInfo2, "FujiRawInfo2" } + }; + + const dng_name_table kContaxTagNames [] = + { + { tcContaxHeader, "ContaxHeader" } + }; + + const char *name = NULL; + + if (parentCode == 0 || + parentCode == tcExifIFD || + parentCode == tcLeafMOS || + (parentCode >= tcFirstSubIFD && parentCode <= tcLastSubIFD) || + (parentCode >= tcFirstChainedIFD && parentCode <= tcLastChainedIFD)) + { + + name = LookupName (tagCode, + kTagNames, + sizeof (kTagNames ) / + sizeof (kTagNames [0])); + + } + + else if (parentCode == tcGPSInfo) + { + + name = LookupName (tagCode, + kGPSTagNames, + sizeof (kGPSTagNames ) / + sizeof (kGPSTagNames [0])); + + } + + else if (parentCode == tcInteroperabilityIFD) + { + + name = LookupName (tagCode, + kInteroperabilityTagNames, + sizeof (kInteroperabilityTagNames ) / + sizeof (kInteroperabilityTagNames [0])); + + } + + else if (parentCode == tcFujiRAF) + { + + name = LookupName (tagCode, + kFujiTagNames, + sizeof (kFujiTagNames ) / + sizeof (kFujiTagNames [0])); + + } + + else if (parentCode == tcContaxRAW) + { + + name = LookupName (tagCode, + kContaxTagNames, + sizeof (kContaxTagNames ) / + sizeof (kContaxTagNames [0])); + + } + + if (name) + { + return name; + } + + static char s [32]; + + if (parentCode == tcCanonCRW) + { + sprintf (s, "CRW_%04X", (unsigned) tagCode); + } + + else if (parentCode == tcMinoltaMRW) + { + + char c1 = (char) ((tagCode >> 24) & 0xFF); + char c2 = (char) ((tagCode >> 16) & 0xFF); + char c3 = (char) ((tagCode >> 8) & 0xFF); + char c4 = (char) ((tagCode ) & 0xFF); + + if (c1 < ' ') c1 = '_'; + if (c2 < ' ') c2 = '_'; + if (c3 < ' ') c3 = '_'; + if (c4 < ' ') c4 = '_'; + + sprintf (s, "MRW%c%c%c%c", c1, c2, c3, c4); + + } + + else if (parentCode == tcFujiRawInfo1) + { + sprintf (s, "RAF1_%04X", (unsigned) tagCode); + } + + else if (parentCode == tcFujiRawInfo2) + { + sprintf (s, "RAF2_%04X", (unsigned) tagCode); + } + + else + { + sprintf (s, "Tag%u", (unsigned) tagCode); + } + + return s; + + } + +/*****************************************************************************/ + +const char * LookupTagType (uint32 tagType) + { + + const dng_name_table kTagTypeNames [] = + { + { ttByte, "Byte" }, + { ttAscii, "ASCII" }, + { ttShort, "Short" }, + { ttLong, "Long" }, + { ttRational, "Rational" }, + { ttSByte, "SByte" }, + { ttUndefined, "Undefined" }, + { ttSShort, "SShort" }, + { ttSLong, "SLong" }, + { ttSRational, "SRational" }, + { ttFloat, "Float" }, + { ttDouble, "Double" }, + { ttIFD, "IFD" }, + { ttUnicode, "Unicode" }, + { ttComplex, "Complex" } + }; + + const char *name = LookupName (tagType, + kTagTypeNames, + sizeof (kTagTypeNames ) / + sizeof (kTagTypeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "Type%u", (unsigned) tagType); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupNewSubFileType (uint32 key) + { + + const dng_name_table kNewSubFileTypeNames [] = + { + { sfMainImage , "Main Image" }, + { sfPreviewImage , "Preview Image" }, + { sfTransparencyMask , "Transparency Mask" }, + { sfPreviewMask , "Preview Mask" }, + { sfAltPreviewImage , "Alt Preview Image" } + }; + + const char *name = LookupName (key, + kNewSubFileTypeNames, + sizeof (kNewSubFileTypeNames ) / + sizeof (kNewSubFileTypeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupCompression (uint32 key) + { + + const dng_name_table kCompressionNames [] = + { + { ccUncompressed, "Uncompressed" }, + { ccLZW, "LZW" }, + { ccOldJPEG, "Old JPEG" }, + { ccJPEG, "JPEG" }, + { ccDeflate, "Deflate" }, + { ccPackBits, "PackBits" }, + { ccOldDeflate, "OldDeflate" }, + { ccLossyJPEG, "Lossy JPEG" } + }; + + const char *name = LookupName (key, + kCompressionNames, + sizeof (kCompressionNames ) / + sizeof (kCompressionNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupPredictor (uint32 key) + { + + const dng_name_table kPredictorNames [] = + { + { cpNullPredictor, "NullPredictor" }, + { cpHorizontalDifference, "HorizontalDifference" }, + { cpFloatingPoint, "FloatingPoint" }, + { cpHorizontalDifferenceX2, "HorizontalDifferenceX2" }, + { cpHorizontalDifferenceX4, "HorizontalDifferenceX4" }, + { cpFloatingPointX2, "FloatingPointX2" }, + { cpFloatingPointX4, "FloatingPointX4" } + }; + + const char *name = LookupName (key, + kPredictorNames, + sizeof (kPredictorNames ) / + sizeof (kPredictorNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSampleFormat (uint32 key) + { + + const dng_name_table kSampleFormatNames [] = + { + { sfUnsignedInteger, "UnsignedInteger" }, + { sfSignedInteger, "SignedInteger" }, + { sfFloatingPoint, "FloatingPoint" }, + { sfUndefined, "Undefined" } + }; + + const char *name = LookupName (key, + kSampleFormatNames, + sizeof (kSampleFormatNames ) / + sizeof (kSampleFormatNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupPhotometricInterpretation (uint32 key) + { + + const dng_name_table kPhotometricInterpretationNames [] = + { + { piWhiteIsZero, "WhiteIsZero" }, + { piBlackIsZero, "BlackIsZero" }, + { piRGB, "RGB" }, + { piRGBPalette, "RGBPalette" }, + { piTransparencyMask, "TransparencyMask" }, + { piCMYK, "CMYK" }, + { piYCbCr, "YCbCr" }, + { piCIELab, "CIELab" }, + { piICCLab, "ICCLab" }, + { piCFA, "CFA" }, + { piLinearRaw, "LinearRaw" } + }; + + const char *name = LookupName (key, + kPhotometricInterpretationNames, + sizeof (kPhotometricInterpretationNames ) / + sizeof (kPhotometricInterpretationNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupOrientation (uint32 key) + { + + const dng_name_table kOrientationNames [] = + { + { 1, "1 - 0th row is top, 0th column is left" }, + { 2, "2 - 0th row is top, 0th column is right" }, + { 3, "3 - 0th row is bottom, 0th column is right" }, + { 4, "4 - 0th row is bottom, 0th column is left" }, + { 5, "5 - 0th row is left, 0th column is top" }, + { 6, "6 - 0th row is right, 0th column is top" }, + { 7, "7 - 0th row is right, 0th column is bottom" }, + { 8, "8 - 0th row is left, 0th column is bottom" }, + { 9, "9 - unknown" } + }; + + const char *name = LookupName (key, + kOrientationNames, + sizeof (kOrientationNames ) / + sizeof (kOrientationNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupResolutionUnit (uint32 key) + { + + const dng_name_table kResolutionUnitNames [] = + { + { ruNone, "None" }, + { ruInch, "Inch" }, + { ruCM, "cm" }, + { ruMM, "mm" }, + { ruMicroM, "Micrometer" } + }; + + const char *name = LookupName (key, + kResolutionUnitNames, + sizeof (kResolutionUnitNames ) / + sizeof (kResolutionUnitNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupCFAColor (uint32 key) + { + + const dng_name_table kCFAColorNames [] = + { + { 0, "Red" }, + { 1, "Green" }, + { 2, "Blue" }, + { 3, "Cyan" }, + { 4, "Magenta" }, + { 5, "Yellow" }, + { 6, "White" } + }; + + const char *name = LookupName (key, + kCFAColorNames, + sizeof (kCFAColorNames ) / + sizeof (kCFAColorNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "Color%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSensingMethod (uint32 key) + { + + const dng_name_table kSensingMethodNames [] = + { + { 0, "Undefined" }, + { 1, "MonochromeArea" }, + { 2, "OneChipColorArea" }, + { 3, "TwoChipColorArea" }, + { 4, "ThreeChipColorArea" }, + { 5, "ColorSequentialArea" }, + { 6, "MonochromeLinear" }, + { 7, "TriLinear" }, + { 8, "ColorSequentialLinear" } + }; + + const char *name = LookupName (key, + kSensingMethodNames, + sizeof (kSensingMethodNames ) / + sizeof (kSensingMethodNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupExposureProgram (uint32 key) + { + + const dng_name_table kExposureProgramNames [] = + { + { epUnidentified, "Unidentified" }, + { epManual, "Manual" }, + { epProgramNormal, "Program Normal" }, + { epAperturePriority, "Aperture Priority" }, + { epShutterPriority, "Shutter Priority" }, + { epProgramCreative, "Program Creative" }, + { epProgramAction, "Program Action" }, + { epPortraitMode, "Portrait Mode" }, + { epLandscapeMode, "Landscape Mode" } + }; + + const char *name = LookupName (key, + kExposureProgramNames, + sizeof (kExposureProgramNames ) / + sizeof (kExposureProgramNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupMeteringMode (uint32 key) + { + + const dng_name_table kMeteringModeNames [] = + { + { mmUnidentified, "Unknown" }, + { mmAverage, "Average" }, + { mmCenterWeightedAverage, "CenterWeightedAverage" }, + { mmSpot, "Spot" }, + { mmMultiSpot, "MultiSpot" }, + { mmPattern, "Pattern" }, + { mmPartial, "Partial" }, + { mmOther, "Other" } + }; + + const char *name = LookupName (key, + kMeteringModeNames, + sizeof (kMeteringModeNames ) / + sizeof (kMeteringModeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupLightSource (uint32 key) + { + + const dng_name_table kLightSourceNames [] = + { + { lsUnknown, "Unknown" }, + { lsDaylight, "Daylight" }, + { lsFluorescent, "Fluorescent" }, + { lsTungsten, "Tungsten (incandescent light)" }, + { lsFlash, "Flash" }, + { lsFineWeather, "Fine weather" }, + { lsCloudyWeather, "Cloudy weather" }, + { lsShade, "Shade" }, + { lsDaylightFluorescent, "Daylight fluorescent (D 5700 - 7100K)" }, + { lsDayWhiteFluorescent, "Day white fluorescent (N 4600 - 5500K)" }, + { lsCoolWhiteFluorescent, "Cool white fluorescent (W 3800 - 4500K)" }, + { lsWhiteFluorescent, "White fluorescent (WW 3250 - 3800K)" }, + { lsWarmWhiteFluorescent, "Warm white fluorescent (L 2600 - 3250K)" }, + { lsStandardLightA, "Standard light A" }, + { lsStandardLightB, "Standard light B" }, + { lsStandardLightC, "Standard light C" }, + { lsD55, "D55" }, + { lsD65, "D65" }, + { lsD75, "D75" }, + { lsD50, "D50" }, + { lsISOStudioTungsten, "ISO studio tungsten" }, + { lsOther, "Other" } + }; + + const char *name = LookupName (key, + kLightSourceNames, + sizeof (kLightSourceNames ) / + sizeof (kLightSourceNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + if (key & 0x08000) + { + + sprintf (s, "%uK", (unsigned) (key & 0x7FFF)); + + } + + else + { + + sprintf (s, "%u", (unsigned) key); + + } + + return s; + + } + +/*****************************************************************************/ + +const char * LookupColorSpace (uint32 key) + { + + const dng_name_table kColorSpaceNames [] = + { + { 1, "sRGB" }, + { 0xFFFF, "Uncalibrated" } + }; + + const char *name = LookupName (key, + kColorSpaceNames, + sizeof (kColorSpaceNames ) / + sizeof (kColorSpaceNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupFileSource (uint32 key) + { + + const dng_name_table kFileSourceNames [] = + { + { 3, "DSC" } + }; + + const char *name = LookupName (key, + kFileSourceNames, + sizeof (kFileSourceNames ) / + sizeof (kFileSourceNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSceneType (uint32 key) + { + + const dng_name_table kSceneTypeNames [] = + { + { 1, "A directly photographed image" } + }; + + const char *name = LookupName (key, + kSceneTypeNames, + sizeof (kSceneTypeNames ) / + sizeof (kSceneTypeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupCustomRendered (uint32 key) + { + + const dng_name_table kCustomRenderedNames [] = + { + { 0, "Normal process" }, + { 1, "Custom process" } + }; + + const char *name = LookupName (key, + kCustomRenderedNames, + sizeof (kCustomRenderedNames ) / + sizeof (kCustomRenderedNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupExposureMode (uint32 key) + { + + const dng_name_table kExposureModeNames [] = + { + { 0, "Auto exposure" }, + { 1, "Manual exposure" }, + { 2, "Auto bracket" } + }; + + const char *name = LookupName (key, + kExposureModeNames, + sizeof (kExposureModeNames ) / + sizeof (kExposureModeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupWhiteBalance (uint32 key) + { + + const dng_name_table kWhiteBalanceNames [] = + { + { 0, "Auto white balance" }, + { 1, "Manual white balance" } + }; + + const char *name = LookupName (key, + kWhiteBalanceNames, + sizeof (kWhiteBalanceNames ) / + sizeof (kWhiteBalanceNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSceneCaptureType (uint32 key) + { + + const dng_name_table kSceneCaptureTypeNames [] = + { + { 0, "Standard" }, + { 1, "Landscape" }, + { 2, "Portrait" }, + { 3, "Night scene" } + }; + + const char *name = LookupName (key, + kSceneCaptureTypeNames, + sizeof (kSceneCaptureTypeNames ) / + sizeof (kSceneCaptureTypeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupGainControl (uint32 key) + { + + const dng_name_table kGainControlNames [] = + { + { 0, "None" }, + { 1, "Low gain up" }, + { 2, "High gain up" }, + { 3, "Low gain down" }, + { 4, "High gain down" } + }; + + const char *name = LookupName (key, + kGainControlNames, + sizeof (kGainControlNames ) / + sizeof (kGainControlNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupContrast (uint32 key) + { + + const dng_name_table kContrastNames [] = + { + { 0, "Normal" }, + { 1, "Soft" }, + { 2, "Hard" } + }; + + const char *name = LookupName (key, + kContrastNames, + sizeof (kContrastNames ) / + sizeof (kContrastNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSaturation (uint32 key) + { + + const dng_name_table kSaturationNames [] = + { + { 0, "Normal" }, + { 1, "Low saturation" }, + { 2, "High saturation" } + }; + + const char *name = LookupName (key, + kSaturationNames, + sizeof (kSaturationNames ) / + sizeof (kSaturationNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSharpness (uint32 key) + { + + const dng_name_table kSharpnessNames [] = + { + { 0, "Normal" }, + { 1, "Soft" }, + { 2, "Hard" } + }; + + const char *name = LookupName (key, + kSharpnessNames, + sizeof (kSharpnessNames ) / + sizeof (kSharpnessNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSubjectDistanceRange (uint32 key) + { + + const dng_name_table kSubjectDistanceRangeNames [] = + { + { 0, "Unknown" }, + { 1, "Macro" }, + { 2, "Close view" }, + { 3, "Distant view" } + }; + + const char *name = LookupName (key, + kSubjectDistanceRangeNames, + sizeof (kSubjectDistanceRangeNames ) / + sizeof (kSubjectDistanceRangeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupComponent (uint32 key) + { + + const dng_name_table kComponentNames [] = + { + { 0, "-" }, + { 1, "Y" }, + { 2, "Cb" }, + { 3, "Cr" }, + { 4, "R" }, + { 5, "G" }, + { 6, "B" } + }; + + const char *name = LookupName (key, + kComponentNames, + sizeof (kComponentNames ) / + sizeof (kComponentNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupCFALayout (uint32 key) + { + + const dng_name_table kCFALayoutNames [] = + { + { 1, "Rectangular (or square) layout" }, + { 2, "Staggered layout A: even columns are offset down by 1/2 row" }, + { 3, "Staggered layout B: even columns are offset up by 1/2 row" }, + { 4, "Staggered layout C: even rows are offset right by 1/2 column" }, + { 5, "Staggered layout D: even rows are offset left by 1/2 column" }, + { 6, "Staggered layout E: even rows are offset up by 1/2 row, even columns are offset left by 1/2 column" }, + { 7, "Staggered layout F: even rows are offset up by 1/2 row, even columns are offset right by 1/2 column" }, + { 8, "Staggered layout G: even rows are offset down by 1/2 row, even columns are offset left by 1/2 column" }, + { 9, "Staggered layout H: even rows are offset down by 1/2 row, even columns are offset right by 1/2 column" } + }; + + const char *name = LookupName (key, + kCFALayoutNames, + sizeof (kCFALayoutNames ) / + sizeof (kCFALayoutNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupMakerNoteSafety (uint32 key) + { + + const dng_name_table kMakerNoteSafetyNames [] = + { + { 0, "Unsafe" }, + { 1, "Safe" } + }; + + const char *name = LookupName (key, + kMakerNoteSafetyNames, + sizeof (kMakerNoteSafetyNames ) / + sizeof (kMakerNoteSafetyNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupColorimetricReference (uint32 key) + { + + const dng_name_table kColorimetricReferenceNames [] = + { + { crSceneReferred, "Scene Referred" }, + { crICCProfilePCS, "ICC Profile PCS" } + }; + + const char *name = LookupName (key, + kColorimetricReferenceNames, + sizeof (kColorimetricReferenceNames ) / + sizeof (kColorimetricReferenceNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupPreviewColorSpace (uint32 key) + { + + const dng_name_table kPreviewColorSpaceNames [] = + { + { previewColorSpace_Unknown , "Unknown" }, + { previewColorSpace_GrayGamma22, "Gray Gamma 2.2" }, + { previewColorSpace_sRGB , "sRGB" }, + { previewColorSpace_AdobeRGB , "Adobe RGB (1998)" }, + { previewColorSpace_ProPhotoRGB, "Pro Photo RGB" } + }; + + const char *name = LookupName (key, + kPreviewColorSpaceNames, + sizeof (kPreviewColorSpaceNames ) / + sizeof (kPreviewColorSpaceNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupJPEGMarker (uint32 key) + { + + const dng_name_table kJPEGMarkerNames [] = + { + { M_TEM, "TEM" }, + { M_SOF0, "SOF0" }, + { M_SOF1, "SOF1" }, + { M_SOF2, "SOF2" }, + { M_SOF3, "SOF3" }, + { M_DHT, "DHT" }, + { M_SOF5, "SOF5" }, + { M_SOF6, "SOF6" }, + { M_SOF7, "SOF7" }, + { M_JPG, "JPG" }, + { M_SOF9, "SOF9" }, + { M_SOF10, "SOF10" }, + { M_SOF11, "SOF11" }, + { M_DAC, "DAC" }, + { M_SOF13, "SOF13" }, + { M_SOF14, "SOF14" }, + { M_SOF15, "SOF15" }, + { M_RST0, "RST0" }, + { M_RST1, "RST1" }, + { M_RST2, "RST2" }, + { M_RST3, "RST3" }, + { M_RST4, "RST4" }, + { M_RST5, "RST5" }, + { M_RST6, "RST6" }, + { M_RST7, "RST7" }, + { M_SOI, "SOI" }, + { M_EOI, "EOI" }, + { M_SOS, "SOS" }, + { M_DQT, "DQT" }, + { M_DNL, "DNL" }, + { M_DRI, "DRI" }, + { M_DHP, "DHP" }, + { M_EXP, "EXP" }, + { M_APP0, "APP0" }, + { M_APP1, "APP1" }, + { M_APP2, "APP2" }, + { M_APP3, "APP3" }, + { M_APP4, "APP4" }, + { M_APP5, "APP5" }, + { M_APP6, "APP6" }, + { M_APP7, "APP7" }, + { M_APP8, "APP8" }, + { M_APP9, "APP9" }, + { M_APP10, "APP10" }, + { M_APP11, "APP11" }, + { M_APP12, "APP12" }, + { M_APP13, "APP13" }, + { M_APP14, "APP14" }, + { M_APP15, "APP15" }, + { M_JPG0, "JPG0" }, + { M_JPG1, "JPG1" }, + { M_JPG2, "JPG2" }, + { M_JPG3, "JPG3" }, + { M_JPG4, "JPG4" }, + { M_JPG5, "JPG5" }, + { M_JPG6, "JPG6" }, + { M_JPG7, "JPG7" }, + { M_JPG8, "JPG8" }, + { M_JPG9, "JPG9" }, + { M_JPG10, "JPG10" }, + { M_JPG11, "JPG11" }, + { M_JPG12, "JPG12" }, + { M_JPG13, "JPG13" }, + { M_COM, "COM" }, + { M_ERROR, "ERROR" } + }; + + const char *name = LookupName (key, + kJPEGMarkerNames, + sizeof (kJPEGMarkerNames ) / + sizeof (kJPEGMarkerNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "0x%02X", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +const char * LookupSensitivityType (uint32 key) + { + + const dng_name_table kSensitivityTypeNames [] = + { + { stUnknown, "Unknown" }, + { stStandardOutputSensitivity, "Standard Output Sensitivity (SOS)" }, + { stRecommendedExposureIndex, "Recommended Exposure Index (REI)" }, + { stISOSpeed, "ISO Speed" }, + { stSOSandREI, "Standard Output Sensitivity (SOS) and Recommended Exposure Index (REI)" }, + { stSOSandISOSpeed, "Standard Output Sensitivity (SOS) and ISO Speed" }, + { stREIandISOSpeed, "Recommended Exposure Index (REI) and ISO Speed" }, + { stSOSandREIandISOSpeed, "Standard Output Sensitivity (SOS) and Recommended Exposure Index (REI) and ISO Speed" }, + }; + + const char *name = LookupName (key, + kSensitivityTypeNames, + sizeof (kSensitivityTypeNames ) / + sizeof (kSensitivityTypeNames [0])); + + if (name) + { + return name; + } + + static char s [32]; + + sprintf (s, "%u", (unsigned) key); + + return s; + + } + +/*****************************************************************************/ + +void DumpHexAscii (dng_stream &stream, + uint32 count) + { + + uint32 rows = (count + 15) >> 4; + + if (rows > gDumpLineLimit) + rows = gDumpLineLimit; + + for (uint32 row = 0; row < rows; row++) + { + + printf (" "); + + uint32 col; + + uint32 cols = count - (row << 4); + + if (cols > 16) + cols = 16; + + uint8 x [16]; + + for (col = 0; col < 16; col++) + { + + x [col] = ' '; + + if (col < cols) + { + + x [col] = stream.Get_uint8 (); + + printf ("%02x ", x [col]); + + } + + else + { + printf (" "); + } + + } + + printf (" "); + + for (col = 0; col < 16; col++) + { + + if (x [col] >= (uint8) ' ' && x [col] <= (uint8) '~') + { + printf ("%c", x [col]); + } + + else + { + printf ("."); + } + + } + + printf ("\n"); + + } + + if (count > rows * 16) + { + printf (" ... %u more bytes\n", (unsigned) (count - rows * 16)); + } + + } + +/*****************************************************************************/ + +void DumpHexAscii (const uint8 *buf, + uint32 count) + { + + uint32 rows = (count + 15) >> 4; + + if (rows > gDumpLineLimit) + rows = gDumpLineLimit; + + for (uint32 row = 0; row < rows; row++) + { + + printf (" "); + + uint32 col; + + uint32 cols = count - (row << 4); + + if (cols > 16) + cols = 16; + + uint8 x [16]; + + for (col = 0; col < 16; col++) + { + + x [col] = ' '; + + if (col < cols) + { + + x [col] = *(buf++); + + printf ("%02x ", x [col]); + + } + + else + { + printf (" "); + } + + } + + printf (" "); + + for (col = 0; col < 16; col++) + { + + if (x [col] >= (uint8) ' ' && x [col] <= (uint8) '~') + { + printf ("%c", x [col]); + } + + else + { + printf ("."); + } + + } + + printf ("\n"); + + } + + if (count > rows * 16) + { + printf (" ... %u more bytes\n", (unsigned) (count - rows * 16)); + } + + } + +/*****************************************************************************/ + +void DumpXMP (dng_stream &stream, + uint32 count) + { + + uint32 lineLength = 0; + + while (count > 0) + { + + uint32 x = stream.Get_uint8 (); + + if (x == 0) break; + + count--; + + if (lineLength == 0) + { + + printf ("XMP: "); + + lineLength = 5; + + } + + if (x == '\n' || + x == '\r') + { + + printf ("\n"); + + lineLength = 0; + + } + + else + { + + if (lineLength >= 128) + { + + printf ("\nXMP: "); + + lineLength = 5; + + } + + if (x >= ' ' && x <= '~') + { + + printf ("%c", (char) x); + + lineLength += 1; + + } + + else + { + + printf ("\\%03o", (unsigned) x); + + lineLength += 4; + + } + + } + + } + + if (lineLength != 0) + { + + printf ("\n"); + + } + + } + +/*****************************************************************************/ + +void DumpString (const dng_string &s) + { + + const uint32 kMaxDumpString = gDumpLineLimit * 64; + + printf ("\""); + + const char *ss = s.Get (); + + uint32 total = 0; + + while (*ss != 0 && total++ < kMaxDumpString) + { + + uint32 c = dng_string::DecodeUTF8 (ss); + + if (c >= ' ' && c <= '~') + { + printf ("%c", (char) c); + } + + else switch (c) + { + + case '\t': + { + printf ("\\t"); + break; + } + + case '\n': + { + printf ("\\n"); + break; + } + + case '\r': + { + printf ("\\r"); + break; + } + + default: + { + printf ("[%X]", (unsigned) c); + } + + } + + } + + uint32 extra = (uint32) strlen (ss); + + if (extra > 0) + { + printf ("...\" (%u more bytes)", (unsigned) extra); + } + + else + { + printf ("\""); + } + + } + +/*****************************************************************************/ + +void DumpTagValues (dng_stream &stream, + const char *entry_name, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + const char *tag_name) + { + + const uint32 kMaxDumpSingleLine = 4; + + const uint32 kMaxDumpArray = Max_uint32 (gDumpLineLimit, kMaxDumpSingleLine); + + printf ("%s:", tag_name ? tag_name + : LookupTagCode (parentCode, tagCode)); + + switch (tagType) + { + + case ttShort: + case ttLong: + case ttIFD: + case ttSByte: + case ttSShort: + case ttSLong: + case ttRational: + case ttSRational: + case ttFloat: + case ttDouble: + { + + if (tagCount > kMaxDumpSingleLine) + { + + printf (" %u entries", (unsigned) tagCount); + + } + + for (uint32 j = 0; j < tagCount && j < kMaxDumpArray; j++) + { + + if (tagCount <= kMaxDumpSingleLine) + { + + if (j == 0) + { + + printf (" %s =", entry_name); + + } + + printf (" "); + + } + + else + { + + printf ("\n %s [%u] = ", entry_name, (unsigned) j); + + } + + switch (tagType) + { + + case ttByte: + case ttShort: + case ttLong: + case ttIFD: + { + + uint32 x = stream.TagValue_uint32 (tagType); + + printf ("%u", (unsigned) x); + + break; + + } + + case ttSByte: + case ttSShort: + case ttSLong: + { + + int32 x = stream.TagValue_int32 (tagType); + + printf ("%d", (int) x); + + break; + + } + + case ttRational: + { + + dng_urational x = stream.TagValue_urational (tagType); + + printf ("%u/%u", (unsigned) x.n, (unsigned) x.d); + + break; + + } + + case ttSRational: + { + + dng_srational x = stream.TagValue_srational (tagType); + + printf ("%d/%d", (int) x.n, (int) x.d); + + break; + + } + + default: + { + + real64 x = stream.TagValue_real64 (tagType); + + printf ("%f", x); + + } + + } + + } + + printf ("\n"); + + if (tagCount > kMaxDumpArray) + { + + printf (" ... %u more entries\n", (unsigned) (tagCount - kMaxDumpArray)); + + } + + break; + + } + + case ttAscii: + { + + dng_string s; + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + s, + false); + + printf (" "); + + DumpString (s); + + printf ("\n"); + + break; + + } + + default: + { + + uint32 tagSize = tagCount * TagTypeSize (tagType); + + if (tagCount == 1 && (tagType == ttByte || + tagType == ttUndefined)) + { + + uint8 x = stream.Get_uint8 (); + + printf (" %s = %u\n", LookupTagType (tagType), x); + + } + + else + { + + printf (" %s, size = %u\n", LookupTagType (tagType), (unsigned) tagSize); + + DumpHexAscii (stream, tagSize); + + } + + } + + } + + } + +/*****************************************************************************/ + +void DumpMatrix (const dng_matrix &m) + { + + for (uint32 row = 0; row < m.Rows (); row++) + { + + for (uint32 col = 0; col < m.Cols (); col++) + { + + if (col == 0) + printf (" "); + else + printf (" "); + + printf ("%8.4f", m [row] [col]); + + } + + printf ("\n"); + + } + + } + +/*****************************************************************************/ + +void DumpVector (const dng_vector &v) + { + + for (uint32 index = 0; index < v.Count (); index++) + { + + printf (" %0.4f", v [index]); + + } + + printf ("\n"); + + } + +/*****************************************************************************/ + +void DumpDateTime (const dng_date_time &dt) + { + + printf ("%04d:%02d:%02d %02d:%02d:%02d", + (int) dt.fYear, + (int) dt.fMonth, + (int) dt.fDay, + (int) dt.fHour, + (int) dt.fMinute, + (int) dt.fSecond); + + } + +/*****************************************************************************/ + +void DumpExposureTime (real64 x) + { + + if (x > 0.0) + { + + if (x >= 0.25) + { + printf ("%0.2f sec", x); + } + + else if (x >= 0.01) + { + printf ("1/%0.1f sec", 1.0 / x); + } + + else + { + printf ("1/%0.0f sec", 1.0 / x); + } + + } + + else + { + + printf (""); + + } + + } + +/*****************************************************************************/ + +void DumpFingerprint (const dng_fingerprint &p) + { + + printf ("<"); + + for (uint32 j = 0; j < 16; j++) + { + printf ("%02x", p.data [j]); + } + + printf (">"); + + } + +/*****************************************************************************/ + +void DumpHueSatMap (dng_stream &stream, + uint32 hues, + uint32 sats, + uint32 vals, + bool skipSat0) + { + + uint32 doneLines = 0; + uint32 skipLines = 0; + + for (uint32 v = 0; v < vals; v++) + { + + for (uint32 h = 0; h < hues; h++) + { + + for (uint32 s = skipSat0 ? 1 : 0; s < sats; s++) + { + + real32 dh = stream.Get_real32 (); + real32 ds = stream.Get_real32 (); + real32 dv = stream.Get_real32 (); + + if (gDumpLineLimit == 0 || + gDumpLineLimit > doneLines) + { + + doneLines++; + + if (vals == 1) + { + + printf (" h [%2u] s [%2u]: h=%8.4f s=%6.4f v=%6.4f\n", + (unsigned) h, + (unsigned) s, + (double) dh, + (double) ds, + (double) dv); + + } + + else + { + + printf (" v [%2u] h [%2u] s [%2u]: h=%8.4f s=%6.4f v=%6.4f\n", + (unsigned) v, + (unsigned) h, + (unsigned) s, + (double) dh, + (double) ds, + (double) dv); + + } + + } + + else + { + + skipLines++; + + } + + } + + } + + } + + if (skipLines > 0) + { + + printf (" ... %u more entries\n", (unsigned) skipLines); + + } + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +bool CheckTagType (uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint16 validType0, + uint16 validType1, + uint16 validType2, + uint16 validType3) + { + + if (tagType != validType0 && + tagType != validType1 && + tagType != validType2 && + tagType != validType3) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s has unexpected type (%s)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode), + LookupTagType (tagType)); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool CheckTagCount (uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + uint32 minCount, + uint32 maxCount) + { + + if (maxCount < minCount) + maxCount = minCount; + + if (tagCount < minCount || + tagCount > maxCount) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s has unexpected count (%u)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode), + (unsigned) tagCount); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool CheckColorImage (uint32 parentCode, + uint32 tagCode, + uint32 colorPlanes) + { + + if (colorPlanes == 0) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed with unknown color plane count " + " (missing ColorMatrix1 tag?)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + return false; + + } + + if (colorPlanes == 1) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed with monochrome images", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool CheckMainIFD (uint32 parentCode, + uint32 tagCode, + uint32 newSubFileType) + { + + if (newSubFileType != sfMainImage) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed IFDs with NewSubFileType != 0", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool CheckRawIFD (uint32 parentCode, + uint32 tagCode, + uint32 photometricInterpretation) + { + + if (photometricInterpretation != piCFA && + photometricInterpretation != piLinearRaw) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed in IFDs with a non-raw PhotometricInterpretation", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +bool CheckCFA (uint32 parentCode, + uint32 tagCode, + uint32 photometricInterpretation) + { + + if (photometricInterpretation != piCFA) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s is not allowed in IFDs with a non-CFA PhotometricInterpretation", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + return false; + + } + + return true; + + } + +/*****************************************************************************/ + +void ParseStringTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + dng_string &s, + bool trimBlanks) + { + + if (tagCount == 0 || + tagCount == 0xFFFFFFFF) + { + + s.Clear (); + + return; + + } + + dng_memory_data temp_buffer (tagCount + 1); + + char *buffer = temp_buffer.Buffer_char (); + + stream.Get (buffer, tagCount); + + // Make sure the string is null terminated. + + if (buffer [tagCount - 1] != 0) + { + + buffer [tagCount] = 0; + + #if qDNGValidate + + { + + bool hasNull = false; + + for (uint32 j = 0; j < tagCount; j++) + { + + if (buffer [j] == 0) + { + + hasNull = true; + + break; + + } + + } + + if (!hasNull && parentCode < tcFirstMakerNoteIFD) + { + + char message [256]; + + sprintf (message, + "%s %s is not NULL terminated", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + } + + // Medata working group - Allow UTF-8 + + s.Set_UTF8_or_System (buffer); + + if (trimBlanks) + { + + s.TrimTrailingBlanks (); + + } + + } + +/*****************************************************************************/ + +void ParseDualStringTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + dng_string &s1, + dng_string &s2) + { + + if (tagCount == 0 || + tagCount == 0xFFFFFFFF) + { + + s1.Clear (); + s2.Clear (); + + return; + + } + + dng_memory_data temp_buffer (tagCount + 1); + + char *buffer = temp_buffer.Buffer_char (); + + stream.Get (buffer, tagCount); + + // Make sure the string is null terminated. + + if (buffer [tagCount - 1] != 0) + { + + buffer [tagCount] = 0; + + #if qDNGValidate + + { + + uint32 nullCount = 0; + + for (uint32 j = 0; j < tagCount; j++) + { + + if (buffer [j] == 0) + { + + nullCount++; + + } + + } + + if (nullCount < 2 && parentCode < tcFirstMakerNoteIFD) + { + + char message [256]; + + sprintf (message, + "%s %s is not NULL terminated", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + } + + // Medata working group - Allow UTF-8 + + s1.Set_UTF8_or_System (buffer); + + s2.Set_ASCII (NULL); + + for (uint32 j = 1; j < tagCount - 1; j++) + { + + if (buffer [j - 1] != 0 && + buffer [j ] == 0) + { + + // Medata working group - Allow UTF-8 + + s2.Set_UTF8_or_System (buffer + j + 1); + + break; + + } + + } + + s1.TrimTrailingBlanks (); + s2.TrimTrailingBlanks (); + + } + +/*****************************************************************************/ + +void ParseEncodedStringTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + dng_string &s) + { + + if (tagCount < 8) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s has unexpected count (%u)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode), + (unsigned) tagCount); + + ReportWarning (message); + + } + + #else + + (void) parentCode; // Unused + (void) tagCode; // Unused + + #endif + + s.Clear (); + + return; + + } + + char label [8]; + + stream.Get (label, 8); + + // Sometimes lowercase is used by mistake. Accept this, but issue + // warning. + + { + + bool hadLower = false; + + for (uint32 j = 0; j < 8; j++) + { + + if (label [j] >= 'a' && label [j] <= 'z') + { + + label [j] = 'A' + (label [j] - 'a'); + + hadLower = true; + + } + + } + + #if qDNGValidate + + if (hadLower) + { + + char message [256]; + + sprintf (message, + "%s %s text encoding label not all uppercase", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + } + + if (memcmp (label, "UNICODE\000", 8) == 0) + { + + uint32 uChars = (tagCount - 8) >> 1; + + dng_memory_data temp_buffer ((uChars + 1) * 2); + + uint16 *buffer = temp_buffer.Buffer_uint16 (); + + for (uint32 j = 0; j < uChars; j++) + { + + buffer [j] = stream.Get_uint16 (); + + } + + buffer [uChars] = 0; + + #if qDNGValidate + + { + + // If the writer used UTF-8 rather than UTF-16, and padded + // the string with blanks, then there will be lots of 0x2020 + // (unicode dagger symbol) characters in the string. + + uint32 count2020 = 0; + + for (uint32 k = 0; buffer [k] != 0; k++) + { + + if (buffer [k] == 0x2020) + { + + count2020++; + + } + + } + + if (count2020 > 1) + { + + char message [256]; + + sprintf (message, + "%s %s text appears to be UTF-8 rather than UTF-16", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + } + + #endif + + s.Set_UTF16 (buffer); + + } + + else + { + + uint32 aChars = tagCount - 8; + + dng_memory_data temp_buffer (aChars + 1); + + char *buffer = temp_buffer.Buffer_char (); + + stream.Get (buffer, aChars); + + buffer [aChars] = 0; + + enum dng_encoding + { + dng_encoding_ascii, + dng_encoding_jis_x208_1990, + dng_encoding_unknown + }; + + dng_encoding encoding = dng_encoding_unknown; + + if (memcmp (label, "ASCII\000\000\000", 8) == 0) + { + + encoding = dng_encoding_ascii; + + } + + else if (memcmp (label, "JIS\000\000\000\000\000\000", 8) == 0) + { + + encoding = dng_encoding_jis_x208_1990; + + } + + else + { + + // Some Nikon D1 files have UserComment tags with zero encoding bits and + // garbage text values. So don't try to parse tags with unknown text + // encoding unless all the characters are printing ASCII. + + #if qDNGValidate + + if (memcmp (label, "\000\000\000\000\000\000\000\000\000", 8) == 0) + { + + // Many camera makes store null tags with all zero encoding, so + // don't report a warning message for null strings. + + if (buffer [0] != 0) + { + + char message [256]; + + sprintf (message, + "%s %s has unknown encoding", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + } + + else + { + + char message [256]; + + sprintf (message, + "%s %s has unexpected text encoding", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + } + + // If text encoding was unknown, and the text is anything + // other than pure ASCII, then ignore it. + + if (encoding == dng_encoding_unknown) + { + + encoding = dng_encoding_ascii; + + for (uint32 i = 0; i < aChars && buffer [i] != 0; i++) + { + + if (buffer [i] < ' ' || + buffer [i] > '~') + { + + buffer [0] = 0; + + break; + + } + + } + + } + + switch (encoding) + { + + case dng_encoding_ascii: + { + + // Medata working group - allow UTF-8 for ASCII tags. + + s.Set_UTF8_or_System (buffer); + + break; + + } + + case dng_encoding_jis_x208_1990: + { + s.Set_JIS_X208_1990 (buffer); + break; + } + + case dng_encoding_unknown: + { + s.Set_SystemEncoding (buffer); + break; + } + + default: + break; + + } + + #if qDNGValidate + + { + + if (encoding == dng_encoding_ascii && !s.IsASCII ()) + { + + char message [256]; + + sprintf (message, + "%s %s has non-ASCII characters", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + } + + #endif + + } + + s.TrimTrailingBlanks (); + + } + +/*****************************************************************************/ + +bool ParseMatrixTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint32 rows, + uint32 cols, + dng_matrix &m) + { + + if (CheckTagCount (parentCode, tagCode, tagCount, rows * cols)) + { + + dng_matrix temp (rows, cols); + + for (uint32 row = 0; row < rows; row++) + for (uint32 col = 0; col < cols; col++) + { + + temp [row] [col] = stream.TagValue_real64 (tagType); + + } + + m = temp; + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +bool ParseVectorTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint32 count, + dng_vector &v) + { + + if (CheckTagCount (parentCode, tagCode, tagCount, count)) + { + + dng_vector temp (count); + + for (uint32 index = 0; index < count; index++) + { + + temp [index] = stream.TagValue_real64 (tagType); + + } + + v = temp; + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +bool ParseDateTimeTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + dng_date_time &dt) + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttAscii)) + { + return false; + } + + // Kludge: Some versions of PaintShop Pro write these fields + // with a length of 21 rather than 20. Otherwise they are + // correctly formated. So relax this test and allow these + // these longer than standard tags to be parsed. + + (void) CheckTagCount (parentCode, tagCode, tagCount, 20); + + if (tagCount < 20) + { + return false; + } + + char s [21]; + + stream.Get (s, 20); + + s [20] = 0; + + // See if this is a valid date/time string. + + if (dt.Parse (s)) + { + return true; + } + + // Accept strings that contain only blanks, colons, and zeros as + // valid "null" dates. + + dt = dng_date_time (); + + for (uint32 index = 0; index < 21; index++) + { + + char c = s [index]; + + if (c == 0) + { + return true; + } + + if (c != ' ' && c != ':' && c != '0') + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s is not a valid date/time", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode)); + + ReportWarning (message); + + } + + #endif + + return false; + + } + + } + + return false; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_parse_utils.h b/source/lib/dng_sdk/dng_parse_utils.h new file mode 100644 index 0000000..89a082f --- /dev/null +++ b/source/lib/dng_sdk/dng_parse_utils.h @@ -0,0 +1,232 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_parse_utils.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_parse_utils__ +#define __dng_parse_utils__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_flags.h" +#include "dng_types.h" +#include "dng_stream.h" +#include "dng_string.h" +#include "dng_matrix.h" + +/*****************************************************************************/ + +#if qDNGValidate + +/*****************************************************************************/ + +const char * LookupParentCode (uint32 parentCode); + +/*****************************************************************************/ + +const char * LookupTagCode (uint32 parentCode, + uint32 tagCode); + +/*****************************************************************************/ + +const char * LookupTagType (uint32 tagType); + +/*****************************************************************************/ + +const char * LookupNewSubFileType (uint32 key); + +const char * LookupCompression (uint32 key); + +const char * LookupPredictor (uint32 key); + +const char * LookupSampleFormat (uint32 key); + +const char * LookupPhotometricInterpretation (uint32 key); + +const char * LookupOrientation (uint32 key); + +const char * LookupResolutionUnit (uint32 key); + +const char * LookupCFAColor (uint32 key); + +const char * LookupSensingMethod (uint32 key); + +const char * LookupExposureProgram (uint32 key); + +const char * LookupMeteringMode (uint32 key); + +const char * LookupLightSource (uint32 key); + +const char * LookupColorSpace (uint32 key); + +const char * LookupFileSource (uint32 key); + +const char * LookupSceneType (uint32 key); + +const char * LookupCustomRendered (uint32 key); + +const char * LookupExposureMode (uint32 key); + +const char * LookupWhiteBalance (uint32 key); + +const char * LookupSceneCaptureType (uint32 key); + +const char * LookupGainControl (uint32 key); + +const char * LookupContrast (uint32 key); + +const char * LookupSaturation (uint32 key); + +const char * LookupSharpness (uint32 key); + +const char * LookupSubjectDistanceRange (uint32 key); + +const char * LookupComponent (uint32 key); + +const char * LookupCFALayout (uint32 key); + +const char * LookupMakerNoteSafety (uint32 key); + +const char * LookupColorimetricReference (uint32 key); + +const char * LookupPreviewColorSpace (uint32 key); + +const char * LookupJPEGMarker (uint32 key); + +const char * LookupSensitivityType (uint32 key); + +/*****************************************************************************/ + +void DumpHexAscii (dng_stream &stream, + uint32 count); + +void DumpHexAscii (const uint8 *buf, + uint32 count); + +void DumpXMP (dng_stream &stream, + uint32 count); + +void DumpString (const dng_string &s); + +void DumpTagValues (dng_stream &stream, + const char *entry_name, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + const char *tag_name = NULL); + +void DumpMatrix (const dng_matrix &m); + +void DumpVector (const dng_vector &v); + +void DumpDateTime (const dng_date_time &dt); + +void DumpExposureTime (real64 x); + +void DumpFingerprint (const dng_fingerprint &p); + +void DumpHueSatMap (dng_stream &stream, + uint32 hues, + uint32 sats, + uint32 vals, + bool skipSat0); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +bool CheckTagType (uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint16 validType0, + uint16 validType1 = 0, + uint16 validType2 = 0, + uint16 validType3 = 0); + +bool CheckTagCount (uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + uint32 minCount, + uint32 maxCount = 0); + +bool CheckColorImage (uint32 parentCode, + uint32 tagCode, + uint32 colorPlanes); + +bool CheckMainIFD (uint32 parentCode, + uint32 tagCode, + uint32 newSubFileType); + +bool CheckRawIFD (uint32 parentCode, + uint32 tagCode, + uint32 photometricInterpretation); + +bool CheckCFA (uint32 parentCode, + uint32 tagCode, + uint32 photometricInterpretation); + +/*****************************************************************************/ + +void ParseStringTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + dng_string &s, + bool trimBlanks = true); + +void ParseDualStringTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + dng_string &s1, + dng_string &s2); + +void ParseEncodedStringTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagCount, + dng_string &s); + +bool ParseMatrixTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint32 rows, + uint32 cols, + dng_matrix &m); + +bool ParseVectorTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint32 count, + dng_vector &v); + +bool ParseDateTimeTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + dng_date_time &dt); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_pixel_buffer.cpp b/source/lib/dng_sdk/dng_pixel_buffer.cpp new file mode 100644 index 0000000..354a774 --- /dev/null +++ b/source/lib/dng_sdk/dng_pixel_buffer.cpp @@ -0,0 +1,1819 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_pixel_buffer.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_pixel_buffer.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_tag_types.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +void OptimizeOrder (const void *&sPtr, + void *&dPtr, + uint32 sPixelSize, + uint32 dPixelSize, + uint32 &count0, + uint32 &count1, + uint32 &count2, + int32 &sStep0, + int32 &sStep1, + int32 &sStep2, + int32 &dStep0, + int32 &dStep1, + int32 &dStep2) + { + + uint32 step0; + uint32 step1; + uint32 step2; + + // Optimize the order for the data that is most spread out. + + uint32 sRange = Abs_int32 (sStep0) * (count0 - 1) + + Abs_int32 (sStep1) * (count1 - 1) + + Abs_int32 (sStep2) * (count2 - 1); + + uint32 dRange = Abs_int32 (dStep0) * (count0 - 1) + + Abs_int32 (dStep1) * (count1 - 1) + + Abs_int32 (dStep2) * (count2 - 1); + + if (dRange >= sRange) + { + + if (dStep0 < 0) + { + + sPtr = (const void *) + (((const uint8 *) sPtr) + (int32)(count0 - 1) * sStep0 * (int32)sPixelSize); + + dPtr = (void *) + (((uint8 *) dPtr) + (int32)(count0 - 1) * dStep0 * (int32)dPixelSize); + + sStep0 = -sStep0; + dStep0 = -dStep0; + + } + + if (dStep1 < 0) + { + + sPtr = (const void *) + (((const uint8 *) sPtr) + (int32)(count1 - 1) * sStep1 * (int32)sPixelSize); + + dPtr = (void *) + (((uint8 *) dPtr) + (int32)(count1 - 1) * dStep1 * (int32)dPixelSize); + + sStep1 = -sStep1; + dStep1 = -dStep1; + + } + + if (dStep2 < 0) + { + + sPtr = (const void *) + (((const uint8 *) sPtr) + (int32)(count2 - 1) * sStep2 * (int32)sPixelSize); + + dPtr = (void *) + (((uint8 *) dPtr) + (int32)(count2 - 1) * dStep2 * (int32)dPixelSize); + + sStep2 = -sStep2; + dStep2 = -dStep2; + + } + + step0 = (uint32) dStep0; + step1 = (uint32) dStep1; + step2 = (uint32) dStep2; + + } + + else + { + + if (sStep0 < 0) + { + + sPtr = (const void *) + (((const uint8 *) sPtr) + (int32)(count0 - 1) * sStep0 * (int32)sPixelSize); + + dPtr = (void *) + (((uint8 *) dPtr) + (int32)(count0 - 1) * dStep0 * (int32)dPixelSize); + + sStep0 = -sStep0; + dStep0 = -dStep0; + + } + + if (sStep1 < 0) + { + + sPtr = (const void *) + (((const uint8 *) sPtr) + (int32)(count1 - 1) * sStep1 * (int32)sPixelSize); + + dPtr = (void *) + (((uint8 *) dPtr) + (int32)(count1 - 1) * dStep1 * (int32)dPixelSize); + + sStep1 = -sStep1; + dStep1 = -dStep1; + + } + + if (sStep2 < 0) + { + + sPtr = (const void *) + (((const uint8 *) sPtr) + (int32)(count2 - 1) * sStep2 * (int32)sPixelSize); + + dPtr = (void *) + (((uint8 *) dPtr) + (int32)(count2 - 1) * dStep2 * (int32)dPixelSize); + + sStep2 = -sStep2; + dStep2 = -dStep2; + + } + + step0 = (uint32) sStep0; + step1 = (uint32) sStep1; + step2 = (uint32) sStep2; + + } + + if (count0 == 1) step0 = 0xFFFFFFFF; + if (count1 == 1) step1 = 0xFFFFFFFF; + if (count2 == 1) step2 = 0xFFFFFFFF; + + uint32 index0; + uint32 index1; + uint32 index2; + + if (step0 >= step1) + { + + if (step1 >= step2) + { + index0 = 0; + index1 = 1; + index2 = 2; + } + + else if (step2 >= step0) + { + index0 = 2; + index1 = 0; + index2 = 1; + } + + else + { + index0 = 0; + index1 = 2; + index2 = 1; + } + + } + + else + { + + if (step0 >= step2) + { + index0 = 1; + index1 = 0; + index2 = 2; + } + + else if (step2 >= step1) + { + index0 = 2; + index1 = 1; + index2 = 0; + } + + else + { + index0 = 1; + index1 = 2; + index2 = 0; + } + + } + + uint32 count [3]; + + count [0] = count0; + count [1] = count1; + count [2] = count2; + + count0 = count [index0]; + count1 = count [index1]; + count2 = count [index2]; + + int32 step [3]; + + step [0] = sStep0; + step [1] = sStep1; + step [2] = sStep2; + + sStep0 = step [index0]; + sStep1 = step [index1]; + sStep2 = step [index2]; + + step [0] = dStep0; + step [1] = dStep1; + step [2] = dStep2; + + dStep0 = step [index0]; + dStep1 = step [index1]; + dStep2 = step [index2]; + + if (sStep0 == ((int32) count1) * sStep1 && + dStep0 == ((int32) count1) * dStep1) + { + count1 *= count0; + count0 = 1; + } + + if (sStep1 == ((int32) count2) * sStep2 && + dStep1 == ((int32) count2) * dStep2) + { + count2 *= count1; + count1 = 1; + } + + } + +/*****************************************************************************/ + +void OptimizeOrder (const void *&sPtr, + uint32 sPixelSize, + uint32 &count0, + uint32 &count1, + uint32 &count2, + int32 &sStep0, + int32 &sStep1, + int32 &sStep2) + { + + void *dPtr = NULL; + + int32 dStep0 = sStep0; + int32 dStep1 = sStep1; + int32 dStep2 = sStep2; + + OptimizeOrder (sPtr, + dPtr, + sPixelSize, + sPixelSize, + count0, + count1, + count2, + sStep0, + sStep1, + sStep2, + dStep0, + dStep1, + dStep2); + + } + +/*****************************************************************************/ + +void OptimizeOrder (void *&dPtr, + uint32 dPixelSize, + uint32 &count0, + uint32 &count1, + uint32 &count2, + int32 &dStep0, + int32 &dStep1, + int32 &dStep2) + { + + const void *sPtr = NULL; + + int32 sStep0 = dStep0; + int32 sStep1 = dStep1; + int32 sStep2 = dStep2; + + OptimizeOrder (sPtr, + dPtr, + dPixelSize, + dPixelSize, + count0, + count1, + count2, + sStep0, + sStep1, + sStep2, + dStep0, + dStep1, + dStep2); + + } + +/*****************************************************************************/ + +dng_pixel_buffer::dng_pixel_buffer () + + : fArea () + , fPlane (0) + , fPlanes (1) + , fRowStep (1) + , fColStep (1) + , fPlaneStep (1) + , fPixelType (ttUndefined) + , fPixelSize (0) + , fData (NULL) + , fDirty (true) + + { + + } + +/*****************************************************************************/ + +dng_pixel_buffer::dng_pixel_buffer (const dng_pixel_buffer &buffer) + + : fArea (buffer.fArea) + , fPlane (buffer.fPlane) + , fPlanes (buffer.fPlanes) + , fRowStep (buffer.fRowStep) + , fColStep (buffer.fColStep) + , fPlaneStep (buffer.fPlaneStep) + , fPixelType (buffer.fPixelType) + , fPixelSize (buffer.fPixelSize) + , fData (buffer.fData) + , fDirty (buffer.fDirty) + + { + + } + +/*****************************************************************************/ + +dng_pixel_buffer & dng_pixel_buffer::operator= (const dng_pixel_buffer &buffer) + { + + fArea = buffer.fArea; + fPlane = buffer.fPlane; + fPlanes = buffer.fPlanes; + fRowStep = buffer.fRowStep; + fColStep = buffer.fColStep; + fPlaneStep = buffer.fPlaneStep; + fPixelType = buffer.fPixelType; + fPixelSize = buffer.fPixelSize; + fPixelType = buffer.fPixelType; + fData = buffer.fData; + fDirty = buffer.fDirty; + + return *this; + + } + +/*****************************************************************************/ + +dng_pixel_buffer::~dng_pixel_buffer () + { + + } + +/*****************************************************************************/ + +#if qDebugPixelType + +void dng_pixel_buffer::CheckPixelType (uint32 pixelType) const + { + + if (fPixelType != pixelType) + { + + DNG_REPORT ("Pixel type access mismatch"); + + } + + } + +#endif + +/*****************************************************************************/ + +uint32 dng_pixel_buffer::PixelRange () const + { + + switch (fPixelType) + { + + case ttByte: + case ttSByte: + { + return 0x0FF; + } + + case ttShort: + case ttSShort: + { + return 0x0FFFF; + } + + case ttLong: + case ttSLong: + { + return 0xFFFFFFFF; + } + + default: + break; + + } + + return 0; + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::SetConstant (const dng_rect &area, + uint32 plane, + uint32 planes, + uint32 value) + { + + uint32 rows = area.H (); + uint32 cols = area.W (); + + void *dPtr = DirtyPixel (area.t, + area.l, + plane); + + int32 dRowStep = fRowStep; + int32 dColStep = fColStep; + int32 dPlaneStep = fPlaneStep; + + OptimizeOrder (dPtr, + fPixelSize, + rows, + cols, + planes, + dRowStep, + dColStep, + dPlaneStep); + + switch (fPixelSize) + { + + case 1: + { + + if (rows == 1 && cols == 1 && dPlaneStep == 1 && value == 0) + { + + DoZeroBytes (dPtr, planes); + + } + + else + { + + DoSetArea8 ((uint8 *) dPtr, + (uint8) value, + rows, + cols, + planes, + dRowStep, + dColStep, + dPlaneStep); + + } + + break; + + } + + case 2: + { + + if (rows == 1 && cols == 1 && dPlaneStep == 1 && value == 0) + { + + DoZeroBytes (dPtr, planes << 1); + + } + + else + { + + DoSetArea16 ((uint16 *) dPtr, + (uint16) value, + rows, + cols, + planes, + dRowStep, + dColStep, + dPlaneStep); + + } + + break; + + } + + case 4: + { + + if (rows == 1 && cols == 1 && dPlaneStep == 1 && value == 0) + { + + DoZeroBytes (dPtr, planes << 2); + + } + + else + { + + DoSetArea32 ((uint32 *) dPtr, + value, + rows, + cols, + planes, + dRowStep, + dColStep, + dPlaneStep); + + } + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::SetZero (const dng_rect &area, + uint32 plane, + uint32 planes) + { + + uint32 value = 0; + + switch (fPixelType) + { + + case ttByte: + case ttShort: + case ttLong: + case ttFloat: + { + break; + } + + case ttSShort: + { + value = 0x8000; + break; + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + SetConstant (area, + plane, + planes, + value); + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::CopyArea (const dng_pixel_buffer &src, + const dng_rect &area, + uint32 srcPlane, + uint32 dstPlane, + uint32 planes) + { + + uint32 rows = area.H (); + uint32 cols = area.W (); + + const void *sPtr = src.ConstPixel (area.t, + area.l, + srcPlane); + + void *dPtr = DirtyPixel (area.t, + area.l, + dstPlane); + + int32 sRowStep = src.fRowStep; + int32 sColStep = src.fColStep; + int32 sPlaneStep = src.fPlaneStep; + + int32 dRowStep = fRowStep; + int32 dColStep = fColStep; + int32 dPlaneStep = fPlaneStep; + + OptimizeOrder (sPtr, + dPtr, + src.fPixelSize, + fPixelSize, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + if (fPixelType == src.fPixelType) + { + + if (rows == 1 && cols == 1 && sPlaneStep == 1 && dPlaneStep == 1) + { + + DoCopyBytes (sPtr, + dPtr, + planes * fPixelSize); + + } + + else switch (fPixelSize) + { + + case 1: + { + + DoCopyArea8 ((const uint8 *) sPtr, + (uint8 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case 2: + { + + DoCopyArea16 ((const uint16 *) sPtr, + (uint16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case 4: + { + + DoCopyArea32 ((const uint32 *) sPtr, + (uint32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + + else if (src.fPixelType == ttByte) + { + + switch (fPixelType) + { + + case ttShort: + { + + DoCopyArea8_16 ((const uint8 *) sPtr, + (uint16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttSShort: + { + + DoCopyArea8_S16 ((const uint8 *) sPtr, + (int16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttLong: + { + + DoCopyArea8_32 ((const uint8 *) sPtr, + (uint32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttFloat: + { + + DoCopyArea8_R32 ((const uint8 *) sPtr, + (real32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + src.PixelRange ()); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + + else if (src.fPixelType == ttShort) + { + + switch (fPixelType) + { + + case ttByte: + { + + DoCopyArea8 (((const uint8 *) sPtr) + (qDNGBigEndian ? 1 : 0), + (uint8 *) dPtr, + rows, + cols, + planes, + sRowStep << 1, + sColStep << 1, + sPlaneStep << 1, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttSShort: + { + + DoCopyArea16_S16 ((const uint16 *) sPtr, + (int16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttLong: + { + + DoCopyArea16_32 ((const uint16 *) sPtr, + (uint32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttFloat: + { + + DoCopyArea16_R32 ((const uint16 *) sPtr, + (real32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + src.PixelRange ()); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + + else if (src.fPixelType == ttSShort) + { + + switch (fPixelType) + { + + case ttByte: + { + + DoCopyArea8 (((const uint8 *) sPtr) + (qDNGBigEndian ? 1 : 0), + (uint8 *) dPtr, + rows, + cols, + planes, + sRowStep << 1, + sColStep << 1, + sPlaneStep << 1, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttShort: + { + + // Moving between signed 16 bit values and unsigned 16 + // bit values just requires toggling the sign bit. So + // we can use the "backwards" bottleneck. + + DoCopyArea16_S16 ((const uint16 *) sPtr, + (int16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttFloat: + { + + DoCopyAreaS16_R32 ((const int16 *) sPtr, + (real32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + src.PixelRange ()); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + + else if (src.fPixelType == ttLong) + { + + switch (fPixelType) + { + + case ttByte: + { + + DoCopyArea8 (((const uint8 *) sPtr) + (qDNGBigEndian ? 3 : 0), + (uint8 *) dPtr, + rows, + cols, + planes, + sRowStep << 2, + sColStep << 2, + sPlaneStep << 2, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case ttShort: + { + + DoCopyArea16 (((const uint16 *) sPtr) + (qDNGBigEndian ? 1 : 0), + (uint16 *) dPtr, + rows, + cols, + planes, + sRowStep << 1, + sColStep << 1, + sPlaneStep << 1, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + + else if (src.fPixelType == ttFloat) + { + + switch (fPixelType) + { + + case ttByte: + { + + DoCopyAreaR32_8 ((const real32 *) sPtr, + (uint8 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + PixelRange ()); + + break; + + } + + case ttShort: + { + + DoCopyAreaR32_16 ((const real32 *) sPtr, + (uint16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + PixelRange ()); + + break; + + } + + case ttSShort: + { + + DoCopyAreaR32_S16 ((const real32 *) sPtr, + (int16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep, + PixelRange ()); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + + else + { + + ThrowNotYetImplemented (); + + } + + } + +/*****************************************************************************/ + +dng_point dng_pixel_buffer::RepeatPhase (const dng_rect &srcArea, + const dng_rect &dstArea) + { + + int32 repeatV = srcArea.H (); + int32 repeatH = srcArea.W (); + + int32 phaseV; + int32 phaseH; + + if (srcArea.t >= dstArea.t) + { + phaseV = (repeatV - ((srcArea.t - dstArea.t) % repeatV)) % repeatV; + } + else + { + phaseV = (dstArea.t - srcArea.t) % repeatV; + } + + if (srcArea.l >= dstArea.l) + { + phaseH = (repeatH - ((srcArea.l - dstArea.l) % repeatH)) % repeatH; + } + else + { + phaseH = (dstArea.l - srcArea.l) % repeatH; + } + + return dng_point (phaseV, phaseH); + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::RepeatArea (const dng_rect &srcArea, + const dng_rect &dstArea) + { + + dng_point repeat = srcArea.Size (); + + dng_point phase = RepeatPhase (srcArea, + dstArea); + + const void *sPtr = ConstPixel (srcArea.t, + srcArea.l, + fPlane); + + void *dPtr = DirtyPixel (dstArea.t, + dstArea.l, + fPlane); + + uint32 rows = dstArea.H (); + uint32 cols = dstArea.W (); + + switch (fPixelSize) + { + + case 1: + { + + DoRepeatArea8 ((const uint8 *) sPtr, + (uint8 *) dPtr, + rows, + cols, + fPlanes, + fRowStep, + fColStep, + fPlaneStep, + repeat.v, + repeat.h, + phase.v, + phase.h); + + break; + + } + + case 2: + { + + DoRepeatArea16 ((const uint16 *) sPtr, + (uint16 *) dPtr, + rows, + cols, + fPlanes, + fRowStep, + fColStep, + fPlaneStep, + repeat.v, + repeat.h, + phase.v, + phase.h); + + break; + + } + + case 4: + { + + DoRepeatArea32 ((const uint32 *) sPtr, + (uint32 *) dPtr, + rows, + cols, + fPlanes, + fRowStep, + fColStep, + fPlaneStep, + repeat.v, + repeat.h, + phase.v, + phase.h); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + } + + } + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::RepeatSubArea (const dng_rect subArea, + uint32 repeatV, + uint32 repeatH) + { + + if (fArea.t < subArea.t) + { + + RepeatArea (dng_rect (subArea.t , fArea.l, + subArea.t + repeatV, fArea.r), + dng_rect (fArea.t , fArea.l, + subArea.t , fArea.r)); + + } + + if (fArea.b > subArea.b) + { + + RepeatArea (dng_rect (subArea.b - repeatV, fArea.l, + subArea.b , fArea.r), + dng_rect (subArea.b , fArea.l, + fArea.b , fArea.r)); + + } + + if (fArea.l < subArea.l) + { + + RepeatArea (dng_rect (fArea.t, subArea.l , + fArea.b, subArea.l + repeatH), + dng_rect (fArea.t, fArea.l , + fArea.b, subArea.l )); + + } + + if (fArea.r > subArea.r) + { + + RepeatArea (dng_rect (fArea.t, subArea.r - repeatH, + fArea.b, subArea.r ), + dng_rect (fArea.t, subArea.r , + fArea.b, fArea.r )); + + } + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::ShiftRight (uint32 shift) + { + + if (fPixelType != ttShort) + { + + ThrowNotYetImplemented (); + + } + + uint32 rows = fArea.H (); + uint32 cols = fArea.W (); + + uint32 planes = fPlanes; + + void *dPtr = DirtyPixel (fArea.t, + fArea.l, + fPlane); + + const void *sPtr = dPtr; + + int32 sRowStep = fRowStep; + int32 sColStep = fColStep; + int32 sPlaneStep = fPlaneStep; + + int32 dRowStep = fRowStep; + int32 dColStep = fColStep; + int32 dPlaneStep = fPlaneStep; + + OptimizeOrder (sPtr, + dPtr, + fPixelSize, + fPixelSize, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + DoShiftRight16 ((uint16 *) dPtr, + rows, + cols, + planes, + dRowStep, + dColStep, + dPlaneStep, + shift); + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::FlipH () + { + + fData = InternalPixel (fArea.t, fArea.r - 1); + + fColStep = -fColStep; + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::FlipV () + { + + fData = InternalPixel (fArea.b - 1, fArea.l); + + fRowStep = -fRowStep; + + } + +/*****************************************************************************/ + +void dng_pixel_buffer::FlipZ () + { + + fData = InternalPixel (fArea.t, fArea.l, fPlanes - 1); + + fPlaneStep = -fPlaneStep; + + } + +/*****************************************************************************/ + +bool dng_pixel_buffer::EqualArea (const dng_pixel_buffer &src, + const dng_rect &area, + uint32 plane, + uint32 planes) const + { + + uint32 rows = area.H (); + uint32 cols = area.W (); + + const void *sPtr = src.ConstPixel (area.t, + area.l, + plane); + + const void *dPtr = ConstPixel (area.t, + area.l, + plane); + + int32 sRowStep = src.fRowStep; + int32 sColStep = src.fColStep; + int32 sPlaneStep = src.fPlaneStep; + + int32 dRowStep = fRowStep; + int32 dColStep = fColStep; + int32 dPlaneStep = fPlaneStep; + + if (fPixelType == src.fPixelType) + { + + if (rows == 1 && cols == 1 && sPlaneStep == 1 && dPlaneStep == 1) + { + + return DoEqualBytes (sPtr, + dPtr, + planes * fPixelSize); + + } + + else switch (fPixelSize) + { + + case 1: + { + + return DoEqualArea8 ((const uint8 *) sPtr, + (const uint8 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case 2: + { + + return DoEqualArea16 ((const uint16 *) sPtr, + (const uint16 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + case 4: + { + + return DoEqualArea32 ((const uint32 *) sPtr, + (const uint32 *) dPtr, + rows, + cols, + planes, + sRowStep, + sColStep, + sPlaneStep, + dRowStep, + dColStep, + dPlaneStep); + + break; + + } + + default: + { + + ThrowNotYetImplemented (); + + return false; + + } + + } + + } + + else + return false; + + } + +/*****************************************************************************/ + +namespace + { + + template + real64 MaxDiff (const T *src1, + int32 s1RowStep, + int32 s1PlaneStep, + const T *src2, + int32 s2RowStep, + int32 s2PlaneStep, + uint32 rows, + uint32 cols, + uint32 planes) + { + + real64 result = 0.0; + + for (uint32 plane = 0; plane < planes; plane++) + { + + const T *src1Save = src1; + const T *src2Save = src2; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + real64 diff = fabs ((real64)src1 [col] - src2 [col]); + + if (diff > result) + result = diff; + + } + + src1 += s1RowStep; + src2 += s2RowStep; + + } + + src1 = src1Save + s1PlaneStep; + src2 = src2Save + s2PlaneStep; + + } + + return result; + + } + + template + real64 MaxDiff (const T *src1, + int32 s1ColStep, + int32 s1RowStep, + int32 s1PlaneStep, + const T *src2, + int32 s2ColStep, + int32 s2RowStep, + int32 s2PlaneStep, + uint32 rows, + uint32 cols, + uint32 planes) + { + + if (s1ColStep == s2ColStep && + s1ColStep == 1) + return MaxDiff (src1, + s1RowStep, + s1PlaneStep, + src2, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + real64 result = 0.0; + + for (uint32 plane = 0; plane < planes; plane++) + { + + const T *src1Save = src1; + const T *src2Save = src2; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + real64 diff = fabs ((real64)src1 [col * s1ColStep] - src2 [col * s2ColStep]); + + if (diff > result) + result = diff; + + } + + src1 += s1RowStep; + src2 += s2RowStep; + + } + + src1 = src1Save + s1PlaneStep; + src2 = src2Save + s2PlaneStep; + + } + + + return result; + + } + } + +real64 dng_pixel_buffer::MaximumDifference (const dng_pixel_buffer &rhs, + const dng_rect &area, + uint32 plane, + uint32 planes) const + { + + uint32 rows = area.H (); + uint32 cols = area.W (); + + const void *s1Ptr = rhs.ConstPixel (area.t, + area.l, + plane); + + const void *s2Ptr = ConstPixel (area.t, + area.l, + plane); + + int32 s1RowStep = rhs.fRowStep; + int32 s1ColStep = rhs.fColStep; + int32 s1PlaneStep = rhs.fPlaneStep; + + int32 s2RowStep = fRowStep; + int32 s2ColStep = fColStep; + int32 s2PlaneStep = fPlaneStep; + + if (fPixelType == rhs.fPixelType) + { + + switch (fPixelType) + { + + case ttByte: + return MaxDiff ((const uint8 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const uint8 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttShort: + return MaxDiff ((const uint16 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const uint16 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttLong: + return MaxDiff ((const uint32 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const uint32 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttSByte: + return MaxDiff ((const int8 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const int8 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttSShort: + return MaxDiff ((const int16 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const int16 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttSLong: + return MaxDiff ((const int32 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const int32 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttFloat: + return MaxDiff ((const real32 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const real32 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + case ttDouble: + return MaxDiff ((const real64 *)s1Ptr, + s1ColStep, + s1RowStep, + s1PlaneStep, + (const real64 *)s2Ptr, + s2ColStep, + s2RowStep, + s2PlaneStep, + rows, + cols, + planes); + + break; + + + default: + { + + ThrowNotYetImplemented (); + + return 0.0; + + } + + } + + } + + else + ThrowProgramError ("attempt to difference pixel buffers of different formats."); + + return 0.0; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_pixel_buffer.h b/source/lib/dng_sdk/dng_pixel_buffer.h new file mode 100644 index 0000000..346d5be --- /dev/null +++ b/source/lib/dng_sdk/dng_pixel_buffer.h @@ -0,0 +1,680 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_pixel_buffer.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Support for holding buffers of sample data. + */ + +/*****************************************************************************/ + +#ifndef __dng_pixel_buffer__ +#define __dng_pixel_buffer__ + +/*****************************************************************************/ + +#include "dng_assertions.h" +#include "dng_rect.h" +#include "dng_tag_types.h" + +/*****************************************************************************/ + +/// Compute best set of step values for a given source and destination area and stride. + +void OptimizeOrder (const void *&sPtr, + void *&dPtr, + uint32 sPixelSize, + uint32 dPixelSize, + uint32 &count0, + uint32 &count1, + uint32 &count2, + int32 &sStep0, + int32 &sStep1, + int32 &sStep2, + int32 &dStep0, + int32 &dStep1, + int32 &dStep2); + +void OptimizeOrder (const void *&sPtr, + uint32 sPixelSize, + uint32 &count0, + uint32 &count1, + uint32 &count2, + int32 &sStep0, + int32 &sStep1, + int32 &sStep2); + +void OptimizeOrder (void *&dPtr, + uint32 dPixelSize, + uint32 &count0, + uint32 &count1, + uint32 &count2, + int32 &dStep0, + int32 &dStep1, + int32 &dStep2); + +/*****************************************************************************/ + +#define qDebugPixelType 0 + +#if qDebugPixelType + +#define ASSERT_PIXEL_TYPE(typeVal) CheckPixelType (typeVal) + +#else + +#define ASSERT_PIXEL_TYPE(typeVal) DNG_ASSERT (fPixelType == typeVal, "Pixel type access mismatch") + +#endif + +/*****************************************************************************/ + +/// \brief Holds a buffer of pixel data with "pixel geometry" metadata. +/// +/// The pixel geometry describes the layout in terms of how many planes, rows and columns +/// plus the steps (in bytes) between each column, row and plane. + +class dng_pixel_buffer + { + + public: + + // Area this buffer holds. + + dng_rect fArea; + + // Range of planes this buffer holds. + + uint32 fPlane; + uint32 fPlanes; + + // Steps between pixels. + + int32 fRowStep; + int32 fColStep; + int32 fPlaneStep; + + // Basic pixel type (TIFF tag type code). + + uint32 fPixelType; + + // Size of pixel type in bytes. + + uint32 fPixelSize; + + // Pointer to buffer's data. + + void *fData; + + // Do we have write-access to this data? + + bool fDirty; + + private: + + void * InternalPixel (int32 row, + int32 col, + uint32 plane = 0) const + { + + return (void *) + (((uint8 *) fData) + (int32)fPixelSize * + (fRowStep * (row - fArea.t) + + fColStep * (col - fArea.l) + + fPlaneStep * (int32)(plane - fPlane ))); + + } + + #if qDebugPixelType + + void CheckPixelType (uint32 pixelType) const; + + #endif + + public: + + dng_pixel_buffer (); + + dng_pixel_buffer (const dng_pixel_buffer &buffer); + + dng_pixel_buffer & operator= (const dng_pixel_buffer &buffer); + + virtual ~dng_pixel_buffer (); + + /// Get the range of pixel values. + /// \retval Range of value a pixel can take. (Meaning [0, max] for unsigned case. Signed case is biased so [-32768, max - 32768].) + + uint32 PixelRange () const; + + /// Get extent of pixels in buffer + /// \retval Rectangle giving valid extent of buffer. + + const dng_rect & Area () const + { + return fArea; + } + + /// Number of planes of image data. + /// \retval Number of planes held in buffer. + + uint32 Planes () const + { + return fPlanes; + } + + /// Step, in pixels not bytes, between rows of data in buffer. + /// \retval row step in pixels. May be negative. + + int32 RowStep () const + { + return fRowStep; + } + + /// Step, in pixels not bytes, between planes of data in buffer. + /// \retval plane step in pixels. May be negative. + + int32 PlaneStep () const + { + return fPlaneStep; + } + + /// Get read-only untyped (void *) pointer to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as void *. + + const void * ConstPixel (int32 row, + int32 col, + uint32 plane = 0) const + { + + return InternalPixel (row, col, plane); + + } + + /// Get a writable untyped (void *) pointer to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as void *. + + void * DirtyPixel (int32 row, + int32 col, + uint32 plane = 0) + { + + DNG_ASSERT (fDirty, "Dirty access to const pixel buffer"); + + return InternalPixel (row, col, plane); + + } + + /// Get read-only uint8 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as uint8 *. + + const uint8 * ConstPixel_uint8 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttByte); + + return (const uint8 *) ConstPixel (row, col, plane); + + } + + /// Get a writable uint8 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as uint8 *. + + uint8 * DirtyPixel_uint8 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttByte); + + return (uint8 *) DirtyPixel (row, col, plane); + + } + + /// Get read-only int8 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as int8 *. + + const int8 * ConstPixel_int8 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttSByte); + + return (const int8 *) ConstPixel (row, col, plane); + + } + + /// Get a writable int8 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as int8 *. + + int8 * DirtyPixel_int8 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttSByte); + + return (int8 *) DirtyPixel (row, col, plane); + + } + + /// Get read-only uint16 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as uint16 *. + + const uint16 * ConstPixel_uint16 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttShort); + + return (const uint16 *) ConstPixel (row, col, plane); + + } + + /// Get a writable uint16 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as uint16 *. + + uint16 * DirtyPixel_uint16 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttShort); + + return (uint16 *) DirtyPixel (row, col, plane); + + } + + /// Get read-only int16 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as int16 *. + + const int16 * ConstPixel_int16 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttSShort); + + return (const int16 *) ConstPixel (row, col, plane); + + } + + /// Get a writable int16 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as int16 *. + + int16 * DirtyPixel_int16 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttSShort); + + return (int16 *) DirtyPixel (row, col, plane); + + } + + /// Get read-only uint32 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as uint32 *. + + const uint32 * ConstPixel_uint32 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttLong); + + return (const uint32 *) ConstPixel (row, col, plane); + + } + + /// Get a writable uint32 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as uint32 *. + + uint32 * DirtyPixel_uint32 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttLong); + + return (uint32 *) DirtyPixel (row, col, plane); + + } + + /// Get read-only int32 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as int32 *. + + const int32 * ConstPixel_int32 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttSLong); + + return (const int32 *) ConstPixel (row, col, plane); + + } + + /// Get a writable int32 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as int32 *. + + int32 * DirtyPixel_int32 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttSLong); + + return (int32 *) DirtyPixel (row, col, plane); + + } + + /// Get read-only real32 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as real32 *. + + const real32 * ConstPixel_real32 (int32 row, + int32 col, + uint32 plane = 0) const + { + + ASSERT_PIXEL_TYPE (ttFloat); + + return (const real32 *) ConstPixel (row, col, plane); + + } + + /// Get a writable real32 * to pixel data starting at a specific pixel in the buffer. + /// \param row Start row for buffer pointer. + /// \param col Start column for buffer pointer. + /// \param plane Start plane for buffer pointer. + /// \retval Pointer to pixel data as real32 *. + + real32 * DirtyPixel_real32 (int32 row, + int32 col, + uint32 plane = 0) + { + + ASSERT_PIXEL_TYPE (ttFloat); + + return (real32 *) DirtyPixel (row, col, plane); + + } + + /// Initialize a rectangular area of pixel buffer to a constant. + /// \param area Rectangle of pixel buffer to set. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + /// \param value Constant value to set pixels to. + + void SetConstant (const dng_rect &area, + uint32 plane, + uint32 planes, + uint32 value); + + /// Initialize a rectangular area of pixel buffer to a constant unsigned 8-bit value. + /// \param area Rectangle of pixel buffer to set. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + /// \param value Constant uint8 value to set pixels to. + + void SetConstant_uint8 (const dng_rect &area, + uint32 plane, + uint32 planes, + uint8 value) + { + + DNG_ASSERT (fPixelType == ttByte, "Mismatched pixel type"); + + SetConstant (area, plane, planes, (uint32) value); + + } + + /// Initialize a rectangular area of pixel buffer to a constant unsigned 16-bit value. + /// \param area Rectangle of pixel buffer to set. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + /// \param value Constant uint16 value to set pixels to. + + void SetConstant_uint16 (const dng_rect &area, + uint32 plane, + uint32 planes, + uint16 value) + { + + DNG_ASSERT (fPixelType == ttShort, "Mismatched pixel type"); + + SetConstant (area, plane, planes, (uint32) value); + + } + + /// Initialize a rectangular area of pixel buffer to a constant signed 16-bit value. + /// \param area Rectangle of pixel buffer to set. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + /// \param value Constant int16 value to set pixels to. + + void SetConstant_int16 (const dng_rect &area, + uint32 plane, + uint32 planes, + int16 value) + { + + DNG_ASSERT (fPixelType == ttSShort, "Mismatched pixel type"); + + SetConstant (area, plane, planes, (uint32) (uint16) value); + + } + + /// Initialize a rectangular area of pixel buffer to a constant unsigned 32-bit value. + /// \param area Rectangle of pixel buffer to set. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + /// \param value Constant uint32 value to set pixels to. + + void SetConstant_uint32 (const dng_rect &area, + uint32 plane, + uint32 planes, + uint32 value) + { + + DNG_ASSERT (fPixelType == ttLong, "Mismatched pixel type"); + + SetConstant (area, plane, planes, value); + + } + + /// Initialize a rectangular area of pixel buffer to a constant real 32-bit value. + /// \param area Rectangle of pixel buffer to set. + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + /// \param value Constant real32 value to set pixels to. + + void SetConstant_real32 (const dng_rect &area, + uint32 plane, + uint32 planes, + real32 value) + { + + DNG_ASSERT (fPixelType == ttFloat, "Mismatched pixel type"); + + union + { + uint32 i; + real32 f; + } x; + + x.f = value; + + SetConstant (area, plane, planes, x.i); + + } + + /// Initialize a rectangular area of pixel buffer to zeros. + /// \param area Rectangle of pixel buffer to zero. + /// \param area Area to zero + /// \param plane Plane to start filling on. + /// \param planes Number of planes to fill. + + void SetZero (const dng_rect &area, + uint32 plane, + uint32 planes); + + /// Copy image data from an area of one pixel buffer to same area of another. + /// \param src Buffer to copy from. + /// \param area Rectangle of pixel buffer to copy. + /// \param srcPlane Plane to start copy in src. + /// \param dstPlane Plane to start copy in dst. + /// \param planes Number of planes to copy. + + void CopyArea (const dng_pixel_buffer &src, + const dng_rect &area, + uint32 srcPlane, + uint32 dstPlane, + uint32 planes); + + /// Copy image data from an area of one pixel buffer to same area of another. + /// \param src Buffer to copy from. + /// \param area Rectangle of pixel buffer to copy. + /// \param plane Plane to start copy in src and this. + /// \param planes Number of planes to copy. + + void CopyArea (const dng_pixel_buffer &src, + const dng_rect &area, + uint32 plane, + uint32 planes) + { + + CopyArea (src, area, plane, plane, planes); + + } + + /// Calculate the offset phase of destination rectangle relative to source rectangle. + /// Phase is based on a 0,0 origin and the notion of repeating srcArea across dstArea. + /// It is the number of pixels into srcArea to start repeating from when tiling dstArea. + /// \retval dng_point containing horizontal and vertical phase. + + static dng_point RepeatPhase (const dng_rect &srcArea, + const dng_rect &dstArea); + + /// Repeat the image data in srcArea across dstArea. + /// (Generally used for padding operations.) + /// \param srcArea Area to repeat from. + /// \param dstArea Area to fill with data from srcArea. + + void RepeatArea (const dng_rect &srcArea, + const dng_rect &dstArea); + + /// Replicates a sub-area of a buffer to fill the entire buffer. + + void RepeatSubArea (const dng_rect subArea, + uint32 repeatV = 1, + uint32 repeatH = 1); + + /// Apply a right shift (C++ oerpator >>) to all pixel values. Only implemented for 16-bit (signed or unsigned) pixel buffers. + /// \param shift Number of bits by which to right shift each pixel value. + + void ShiftRight (uint32 shift); + + /// Change metadata so pixels are iterated in opposite horizontal order. + /// This operation does not require movement of actual pixel data. + + void FlipH (); + + /// Change metadata so pixels are iterated in opposite vertical order. + /// This operation does not require movement of actual pixel data. + + void FlipV (); + + /// Change metadata so pixels are iterated in opposite plane order. + /// This operation does not require movement of actual pixel data. + + void FlipZ (); // Flip planes + + /// Return true if the contents of an area of the pixel buffer area are the same as those of another. + /// \param rhs Buffer to compare against. + /// \param area Rectangle of pixel buffer to test. + /// \param plane Plane to start comparing. + /// \param planes Number of planes to compare. + /// \retval bool true if areas are equal, false otherwise. + + bool EqualArea (const dng_pixel_buffer &rhs, + const dng_rect &area, + uint32 plane, + uint32 planes) const; + + /// Return the absolute value of the maximum difference between two pixel buffers. Used for comparison testing with tolerance + /// \param rhs Buffer to compare against. + /// \param area Rectangle of pixel buffer to test. + /// \param plane Plane to start comparing. + /// \param planes Number of planes to compare. + /// \retval larges absolute value difference between the corresponding pixels each buffer across area. + + real64 MaximumDifference (const dng_pixel_buffer &rhs, + const dng_rect &area, + uint32 plane, + uint32 planes) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_point.cpp b/source/lib/dng_sdk/dng_point.cpp new file mode 100644 index 0000000..abfd7dd --- /dev/null +++ b/source/lib/dng_sdk/dng_point.cpp @@ -0,0 +1,22 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_point.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_point.h" + +/*****************************************************************************/ + +// Currently all inlined. + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_point.h b/source/lib/dng_sdk/dng_point.h new file mode 100644 index 0000000..476a71d --- /dev/null +++ b/source/lib/dng_sdk/dng_point.h @@ -0,0 +1,198 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_point.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_point__ +#define __dng_point__ + +/*****************************************************************************/ + +#include "dng_types.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +class dng_point + { + + public: + + int32 v; + int32 h; + + public: + + dng_point () + : v (0) + , h (0) + { + } + + dng_point (int32 vv, int32 hh) + : v (vv) + , h (hh) + { + } + + bool operator== (const dng_point &pt) const + { + return (v == pt.v) && + (h == pt.h); + } + + bool operator!= (const dng_point &pt) const + { + return !(*this == pt); + } + + }; + +/*****************************************************************************/ + +class dng_point_real64 + { + + public: + + real64 v; + real64 h; + + public: + + dng_point_real64 () + : v (0.0) + , h (0.0) + { + } + + dng_point_real64 (real64 vv, real64 hh) + : v (vv) + , h (hh) + { + } + + dng_point_real64 (const dng_point &pt) + : v ((real64) pt.v) + , h ((real64) pt.h) + { + } + + bool operator== (const dng_point_real64 &pt) const + { + return (v == pt.v) && + (h == pt.h); + } + + bool operator!= (const dng_point_real64 &pt) const + { + return !(*this == pt); + } + + dng_point Round () const + { + return dng_point (Round_int32 (v), + Round_int32 (h)); + } + + }; + +/*****************************************************************************/ + +inline dng_point operator+ (const dng_point &a, + const dng_point &b) + + + { + + return dng_point (a.v + b.v, + a.h + b.h); + + } + +/*****************************************************************************/ + +inline dng_point_real64 operator+ (const dng_point_real64 &a, + const dng_point_real64 &b) + + + { + + return dng_point_real64 (a.v + b.v, + a.h + b.h); + + } + +/*****************************************************************************/ + +inline dng_point operator- (const dng_point &a, + const dng_point &b) + + + { + + return dng_point (a.v - b.v, + a.h - b.h); + + } + +/*****************************************************************************/ + +inline dng_point_real64 operator- (const dng_point_real64 &a, + const dng_point_real64 &b) + + + { + + return dng_point_real64 (a.v - b.v, + a.h - b.h); + + } + +/*****************************************************************************/ + +inline real64 DistanceSquared (const dng_point_real64 &a, + const dng_point_real64 &b) + + + { + + dng_point_real64 diff = a - b; + + return (diff.v * diff.v) + (diff.h * diff.h); + + } + +/*****************************************************************************/ + +inline dng_point Transpose (const dng_point &a) + { + + return dng_point (a.h, a.v); + + } + +/*****************************************************************************/ + +inline dng_point_real64 Transpose (const dng_point_real64 &a) + { + + return dng_point_real64 (a.h, a.v); + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_preview.cpp b/source/lib/dng_sdk/dng_preview.cpp new file mode 100644 index 0000000..1a65cb0 --- /dev/null +++ b/source/lib/dng_sdk/dng_preview.cpp @@ -0,0 +1,709 @@ +/*****************************************************************************/ +// Copyright 2007-2011 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_preview.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_preview.h" + +#include "dng_assertions.h" +#include "dng_image.h" +#include "dng_image_writer.h" +#include "dng_memory.h" +#include "dng_stream.h" +#include "dng_tag_codes.h" +#include "dng_tag_values.h" + +/*****************************************************************************/ + +class dng_preview_tag_set: public dng_basic_tag_set + { + + private: + + tag_string fApplicationNameTag; + + tag_string fApplicationVersionTag; + + tag_string fSettingsNameTag; + + dng_fingerprint fSettingsDigest; + + tag_uint8_ptr fSettingsDigestTag; + + tag_uint32 fColorSpaceTag; + + tag_string fDateTimeTag; + + tag_real64 fRawToPreviewGainTag; + + tag_uint32 fCacheVersionTag; + + public: + + dng_preview_tag_set (dng_tiff_directory &directory, + const dng_preview &preview, + const dng_ifd &ifd); + + virtual ~dng_preview_tag_set (); + + }; + +/*****************************************************************************/ + +dng_preview_tag_set::dng_preview_tag_set (dng_tiff_directory &directory, + const dng_preview &preview, + const dng_ifd &ifd) + + : dng_basic_tag_set (directory, ifd) + + , fApplicationNameTag (tcPreviewApplicationName, + preview.fInfo.fApplicationName, + false) + + , fApplicationVersionTag (tcPreviewApplicationVersion, + preview.fInfo.fApplicationVersion, + false) + + , fSettingsNameTag (tcPreviewSettingsName, + preview.fInfo.fSettingsName, + false) + + , fSettingsDigest (preview.fInfo.fSettingsDigest) + + , fSettingsDigestTag (tcPreviewSettingsDigest, + fSettingsDigest.data, + 16) + + , fColorSpaceTag (tcPreviewColorSpace, + preview.fInfo.fColorSpace) + + , fDateTimeTag (tcPreviewDateTime, + preview.fInfo.fDateTime, + true) + + , fRawToPreviewGainTag (tcRawToPreviewGain, + preview.fInfo.fRawToPreviewGain) + + , fCacheVersionTag (tcCacheVersion, + preview.fInfo.fCacheVersion) + + { + + if (preview.fInfo.fApplicationName.NotEmpty ()) + { + + directory.Add (&fApplicationNameTag); + + } + + if (preview.fInfo.fApplicationVersion.NotEmpty ()) + { + + directory.Add (&fApplicationVersionTag); + + } + + if (preview.fInfo.fSettingsName.NotEmpty ()) + { + + directory.Add (&fSettingsNameTag); + + } + + if (preview.fInfo.fSettingsDigest.IsValid ()) + { + + directory.Add (&fSettingsDigestTag); + + } + + if (preview.fInfo.fColorSpace != previewColorSpace_MaxEnum) + { + + directory.Add (&fColorSpaceTag); + + } + + if (preview.fInfo.fDateTime.NotEmpty ()) + { + + directory.Add (&fDateTimeTag); + + } + + if (preview.fInfo.fRawToPreviewGain != 1.0) + { + + directory.Add (&fRawToPreviewGainTag); + + } + + if (preview.fInfo.fCacheVersion != 0) + { + + directory.Add (&fCacheVersionTag); + + } + + } + +/*****************************************************************************/ + +dng_preview_tag_set::~dng_preview_tag_set () + { + + } + +/*****************************************************************************/ + +dng_preview::dng_preview () + + : fInfo () + + { + + } + +/*****************************************************************************/ + +dng_preview::~dng_preview () + { + + } + +/*****************************************************************************/ + +dng_image_preview::dng_image_preview () + + : fImage () + , fIFD () + + { + + } + +/*****************************************************************************/ + +dng_image_preview::~dng_image_preview () + { + + } + +/*****************************************************************************/ + +dng_basic_tag_set * dng_image_preview::AddTagSet (dng_tiff_directory &directory) const + { + + fIFD.fNewSubFileType = fInfo.fIsPrimary ? sfPreviewImage + : sfAltPreviewImage; + + fIFD.fImageWidth = fImage->Width (); + fIFD.fImageLength = fImage->Height (); + + fIFD.fSamplesPerPixel = fImage->Planes (); + + fIFD.fPhotometricInterpretation = fIFD.fSamplesPerPixel == 1 ? piBlackIsZero + : piRGB; + + fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8; + + for (uint32 j = 1; j < fIFD.fSamplesPerPixel; j++) + { + fIFD.fBitsPerSample [j] = fIFD.fBitsPerSample [0]; + } + + fIFD.SetSingleStrip (); + + return new dng_preview_tag_set (directory, *this, fIFD); + + } + +/*****************************************************************************/ + +void dng_image_preview::WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const + { + + writer.WriteImage (host, + fIFD, + basic, + stream, + *fImage.Get ()); + + } + +/*****************************************************************************/ + +class dng_jpeg_preview_tag_set: public dng_preview_tag_set + { + + private: + + dng_urational fCoefficientsData [3]; + + tag_urational_ptr fCoefficientsTag; + + uint16 fSubSamplingData [2]; + + tag_uint16_ptr fSubSamplingTag; + + tag_uint16 fPositioningTag; + + dng_urational fReferenceData [6]; + + tag_urational_ptr fReferenceTag; + + public: + + dng_jpeg_preview_tag_set (dng_tiff_directory &directory, + const dng_jpeg_preview &preview, + const dng_ifd &ifd); + + virtual ~dng_jpeg_preview_tag_set (); + + }; + +/******************************************************************************/ + +dng_jpeg_preview_tag_set::dng_jpeg_preview_tag_set (dng_tiff_directory &directory, + const dng_jpeg_preview &preview, + const dng_ifd &ifd) + + : dng_preview_tag_set (directory, preview, ifd) + + , fCoefficientsTag (tcYCbCrCoefficients, fCoefficientsData, 3) + + , fSubSamplingTag (tcYCbCrSubSampling, fSubSamplingData, 2) + + , fPositioningTag (tcYCbCrPositioning, preview.fYCbCrPositioning) + + , fReferenceTag (tcReferenceBlackWhite, fReferenceData, 6) + + { + + if (preview.fPhotometricInterpretation == piYCbCr) + { + + fCoefficientsData [0] = dng_urational (299, 1000); + fCoefficientsData [1] = dng_urational (587, 1000); + fCoefficientsData [2] = dng_urational (114, 1000); + + directory.Add (&fCoefficientsTag); + + fSubSamplingData [0] = (uint16) preview.fYCbCrSubSampling.h; + fSubSamplingData [1] = (uint16) preview.fYCbCrSubSampling.v; + + directory.Add (&fSubSamplingTag); + + directory.Add (&fPositioningTag); + + fReferenceData [0] = dng_urational ( 0, 1); + fReferenceData [1] = dng_urational (255, 1); + fReferenceData [2] = dng_urational (128, 1); + fReferenceData [3] = dng_urational (255, 1); + fReferenceData [4] = dng_urational (128, 1); + fReferenceData [5] = dng_urational (255, 1); + + directory.Add (&fReferenceTag); + + } + + } + +/*****************************************************************************/ + +dng_jpeg_preview_tag_set::~dng_jpeg_preview_tag_set () + { + + } + +/*****************************************************************************/ + +dng_jpeg_preview::dng_jpeg_preview () + + : fPreviewSize () + , fPhotometricInterpretation (piYCbCr) + , fYCbCrSubSampling (1, 1) + , fYCbCrPositioning (2) + , fCompressedData () + + { + + } + +/*****************************************************************************/ + +dng_jpeg_preview::~dng_jpeg_preview () + { + + } + +/*****************************************************************************/ + +dng_basic_tag_set * dng_jpeg_preview::AddTagSet (dng_tiff_directory &directory) const + { + + dng_ifd ifd; + + ifd.fNewSubFileType = fInfo.fIsPrimary ? sfPreviewImage + : sfAltPreviewImage; + + ifd.fImageWidth = fPreviewSize.h; + ifd.fImageLength = fPreviewSize.v; + + ifd.fPhotometricInterpretation = fPhotometricInterpretation; + + ifd.fBitsPerSample [0] = 8; + ifd.fBitsPerSample [1] = 8; + ifd.fBitsPerSample [2] = 8; + + ifd.fSamplesPerPixel = (fPhotometricInterpretation == piBlackIsZero ? 1 : 3); + + ifd.fCompression = ccJPEG; + ifd.fPredictor = cpNullPredictor; + + ifd.SetSingleStrip (); + + return new dng_jpeg_preview_tag_set (directory, *this, ifd); + + } + +/*****************************************************************************/ + +void dng_jpeg_preview::WriteData (dng_host & /* host */, + dng_image_writer & /* writer */, + dng_basic_tag_set &basic, + dng_stream &stream) const + { + + basic.SetTileOffset (0, (uint32) stream.Position ()); + + basic.SetTileByteCount (0, fCompressedData->LogicalSize ()); + + stream.Put (fCompressedData->Buffer (), + fCompressedData->LogicalSize ()); + + if (fCompressedData->LogicalSize () & 1) + { + stream.Put_uint8 (0); + } + + } + +/*****************************************************************************/ + +void dng_jpeg_preview::SpoolAdobeThumbnail (dng_stream &stream) const + { + + DNG_ASSERT (fCompressedData.Get (), + "SpoolAdobeThumbnail: no data"); + + DNG_ASSERT (fPhotometricInterpretation == piYCbCr, + "SpoolAdobeThumbnail: Non-YCbCr"); + + uint32 compressedSize = fCompressedData->LogicalSize (); + + stream.Put_uint32 (DNG_CHAR4 ('8','B','I','M')); + stream.Put_uint16 (1036); + stream.Put_uint16 (0); + + stream.Put_uint32 (compressedSize + 28); + + uint32 widthBytes = (fPreviewSize.h * 24 + 31) / 32 * 4; + + stream.Put_uint32 (1); + stream.Put_uint32 (fPreviewSize.h); + stream.Put_uint32 (fPreviewSize.v); + stream.Put_uint32 (widthBytes); + stream.Put_uint32 (widthBytes * fPreviewSize.v); + stream.Put_uint32 (compressedSize); + stream.Put_uint16 (24); + stream.Put_uint16 (1); + + stream.Put (fCompressedData->Buffer (), + compressedSize); + + if (compressedSize & 1) + { + stream.Put_uint8 (0); + } + + } + +/*****************************************************************************/ + +class dng_raw_preview_tag_set: public dng_preview_tag_set + { + + private: + + tag_data_ptr fOpcodeList2Tag; + + tag_uint32_ptr fWhiteLevelTag; + + uint32 fWhiteLevelData [kMaxColorPlanes]; + + public: + + dng_raw_preview_tag_set (dng_tiff_directory &directory, + const dng_raw_preview &preview, + const dng_ifd &ifd); + + virtual ~dng_raw_preview_tag_set (); + + }; + +/*****************************************************************************/ + +dng_raw_preview_tag_set::dng_raw_preview_tag_set (dng_tiff_directory &directory, + const dng_raw_preview &preview, + const dng_ifd &ifd) + + : dng_preview_tag_set (directory, preview, ifd) + + , fOpcodeList2Tag (tcOpcodeList2, + ttUndefined, + 0, + NULL) + + , fWhiteLevelTag (tcWhiteLevel, + fWhiteLevelData, + preview.fImage->Planes ()) + + { + + if (preview.fOpcodeList2Data.Get ()) + { + + fOpcodeList2Tag.SetData (preview.fOpcodeList2Data->Buffer ()); + fOpcodeList2Tag.SetCount (preview.fOpcodeList2Data->LogicalSize ()); + + directory.Add (&fOpcodeList2Tag); + + } + + if (preview.fImage->PixelType () == ttFloat) + { + + for (uint32 j = 0; j < kMaxColorPlanes; j++) + { + fWhiteLevelData [j] = 32768; + } + + directory.Add (&fWhiteLevelTag); + + } + + } + +/*****************************************************************************/ + +dng_raw_preview_tag_set::~dng_raw_preview_tag_set () + { + + } + +/*****************************************************************************/ + +dng_raw_preview::dng_raw_preview () + + : fImage () + , fOpcodeList2Data () + , fCompressionQuality (-1) + , fIFD () + + { + + } + +/*****************************************************************************/ + +dng_raw_preview::~dng_raw_preview () + { + + } + +/*****************************************************************************/ + +dng_basic_tag_set * dng_raw_preview::AddTagSet (dng_tiff_directory &directory) const + { + + fIFD.fNewSubFileType = sfPreviewImage; + + fIFD.fImageWidth = fImage->Width (); + fIFD.fImageLength = fImage->Height (); + + fIFD.fSamplesPerPixel = fImage->Planes (); + + fIFD.fPhotometricInterpretation = piLinearRaw; + + if (fImage->PixelType () == ttFloat) + { + + fIFD.fCompression = ccDeflate; + + fIFD.fCompressionQuality = fCompressionQuality; + + fIFD.fPredictor = cpFloatingPoint; + + for (uint32 j = 0; j < fIFD.fSamplesPerPixel; j++) + { + fIFD.fBitsPerSample [j] = 16; + fIFD.fSampleFormat [j] = sfFloatingPoint; + } + + fIFD.FindTileSize (512 * 1024); + + } + + else + { + + fIFD.fCompression = ccLossyJPEG; + + fIFD.fCompressionQuality = fCompressionQuality; + + fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8; + + for (uint32 j = 1; j < fIFD.fSamplesPerPixel; j++) + { + fIFD.fBitsPerSample [j] = fIFD.fBitsPerSample [0]; + } + + fIFD.FindTileSize (512 * 512 * fIFD.fSamplesPerPixel); + + } + + return new dng_raw_preview_tag_set (directory, *this, fIFD); + + } + +/*****************************************************************************/ + +void dng_raw_preview::WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const + { + + writer.WriteImage (host, + fIFD, + basic, + stream, + *fImage.Get ()); + + } + +/*****************************************************************************/ + +dng_mask_preview::dng_mask_preview () + + : fImage () + , fCompressionQuality (-1) + , fIFD () + + { + + } + +/*****************************************************************************/ + +dng_mask_preview::~dng_mask_preview () + { + + } + +/*****************************************************************************/ + +dng_basic_tag_set * dng_mask_preview::AddTagSet (dng_tiff_directory &directory) const + { + + fIFD.fNewSubFileType = sfPreviewMask; + + fIFD.fImageWidth = fImage->Width (); + fIFD.fImageLength = fImage->Height (); + + fIFD.fSamplesPerPixel = 1; + + fIFD.fPhotometricInterpretation = piTransparencyMask; + + fIFD.fCompression = ccDeflate; + fIFD.fPredictor = cpHorizontalDifference; + + fIFD.fCompressionQuality = fCompressionQuality; + + fIFD.fBitsPerSample [0] = TagTypeSize (fImage->PixelType ()) * 8; + + fIFD.FindTileSize (512 * 512 * fIFD.fSamplesPerPixel); + + return new dng_basic_tag_set (directory, fIFD); + + } + +/*****************************************************************************/ + +void dng_mask_preview::WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const + { + + writer.WriteImage (host, + fIFD, + basic, + stream, + *fImage.Get ()); + + } + +/*****************************************************************************/ + +dng_preview_list::dng_preview_list () + + : fCount (0) + + { + + } + +/*****************************************************************************/ + +dng_preview_list::~dng_preview_list () + { + + } + +/*****************************************************************************/ + +void dng_preview_list::Append (AutoPtr &preview) + { + + if (preview.Get ()) + { + + DNG_ASSERT (fCount < kMaxDNGPreviews, "DNG preview list overflow"); + + if (fCount < kMaxDNGPreviews) + { + + fPreview [fCount++] . Reset (preview.Release ()); + + } + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_preview.h b/source/lib/dng_sdk/dng_preview.h new file mode 100644 index 0000000..2b616e3 --- /dev/null +++ b/source/lib/dng_sdk/dng_preview.h @@ -0,0 +1,245 @@ +/*****************************************************************************/ +// Copyright 2007-2011 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_preview.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_preview__ +#define __dng_preview__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_ifd.h" +#include "dng_opcode_list.h" +#include "dng_point.h" +#include "dng_sdk_limits.h" + +/*****************************************************************************/ + +class dng_preview + { + + public: + + dng_preview_info fInfo; + + protected: + + dng_preview (); + + public: + + virtual ~dng_preview (); + + virtual dng_basic_tag_set * AddTagSet (dng_tiff_directory &directory) const = 0; + + virtual void WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const = 0; + + }; + +/*****************************************************************************/ + +class dng_image_preview: public dng_preview + { + + public: + + AutoPtr fImage; + + private: + + mutable dng_ifd fIFD; + + public: + + dng_image_preview (); + + virtual ~dng_image_preview (); + + virtual dng_basic_tag_set * AddTagSet (dng_tiff_directory &directory) const; + + virtual void WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const; + + private: + + // Hidden copy constructor and assignment operator. + + dng_image_preview (const dng_image_preview &preview); + + dng_image_preview & operator= (const dng_image_preview &preview); + + }; + +/*****************************************************************************/ + +class dng_jpeg_preview: public dng_preview + { + + public: + + dng_point fPreviewSize; + + uint16 fPhotometricInterpretation; + + dng_point fYCbCrSubSampling; + + uint16 fYCbCrPositioning; + + AutoPtr fCompressedData; + + public: + + dng_jpeg_preview (); + + virtual ~dng_jpeg_preview (); + + virtual dng_basic_tag_set * AddTagSet (dng_tiff_directory &directory) const; + + virtual void WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const; + + void SpoolAdobeThumbnail (dng_stream &stream) const; + + private: + + // Hidden copy constructor and assignment operator. + + dng_jpeg_preview (const dng_jpeg_preview &preview); + + dng_jpeg_preview & operator= (const dng_jpeg_preview &preview); + + }; + +/*****************************************************************************/ + +class dng_raw_preview: public dng_preview + { + + public: + + AutoPtr fImage; + + AutoPtr fOpcodeList2Data; + + int32 fCompressionQuality; + + private: + + mutable dng_ifd fIFD; + + public: + + dng_raw_preview (); + + virtual ~dng_raw_preview (); + + virtual dng_basic_tag_set * AddTagSet (dng_tiff_directory &directory) const; + + virtual void WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const; + + private: + + // Hidden copy constructor and assignment operator. + + dng_raw_preview (const dng_raw_preview &preview); + + dng_raw_preview & operator= (const dng_raw_preview &preview); + + }; + +/*****************************************************************************/ + +class dng_mask_preview: public dng_preview + { + + public: + + AutoPtr fImage; + + int32 fCompressionQuality; + + private: + + mutable dng_ifd fIFD; + + public: + + dng_mask_preview (); + + virtual ~dng_mask_preview (); + + virtual dng_basic_tag_set * AddTagSet (dng_tiff_directory &directory) const; + + virtual void WriteData (dng_host &host, + dng_image_writer &writer, + dng_basic_tag_set &basic, + dng_stream &stream) const; + + private: + + // Hidden copy constructor and assignment operator. + + dng_mask_preview (const dng_mask_preview &preview); + + dng_mask_preview & operator= (const dng_mask_preview &preview); + + }; + +/*****************************************************************************/ + +class dng_preview_list + { + + private: + + uint32 fCount; + + AutoPtr fPreview [kMaxDNGPreviews]; + + public: + + dng_preview_list (); + + ~dng_preview_list (); + + uint32 Count () const + { + return fCount; + } + + const dng_preview & Preview (uint32 index) const + { + return *(fPreview [index]); + } + + void Append (AutoPtr &preview); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_pthread.cpp b/source/lib/dng_sdk/dng_pthread.cpp new file mode 100644 index 0000000..16005cd --- /dev/null +++ b/source/lib/dng_sdk/dng_pthread.cpp @@ -0,0 +1,1141 @@ +/*****************************************************************************/ +// Copyright 2002-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_pthread.cpp#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +#include "dng_pthread.h" + +/*****************************************************************************/ + +#if qDNGThreadSafe + +/*****************************************************************************/ + +#include "dng_assertions.h" + +/*****************************************************************************/ + +#if qWinOS + +#pragma warning(disable : 4786) + +// Nothing in this file requires Unicode, +// However, CreateSemaphore has a path parameter +// (which is NULL always in this code) and thus +// does not work on Win98 if UNICODE is defined. +// So we force it off here. + +#undef UNICODE +#undef _UNICODE + +#include +#include +#include +#include +#include +#include + +#else + +#include + +#endif + +/*****************************************************************************/ + +#if qWinOS + +/*****************************************************************************/ + +namespace { + struct waiter { + struct waiter *prev; + struct waiter *next; + HANDLE semaphore; + bool chosen_by_signal; + }; +} + +/*****************************************************************************/ + +struct dng_pthread_mutex_impl +{ + CRITICAL_SECTION lock; + + dng_pthread_mutex_impl() { ::InitializeCriticalSection(&lock); } + ~dng_pthread_mutex_impl() { ::DeleteCriticalSection(&lock); } + void Lock() { ::EnterCriticalSection(&lock); } + void Unlock() { ::LeaveCriticalSection(&lock); } +private: + dng_pthread_mutex_impl &operator=(const dng_pthread_mutex_impl &) { } + dng_pthread_mutex_impl(const dng_pthread_mutex_impl &) { } +}; + +/*****************************************************************************/ + +struct dng_pthread_cond_impl +{ + dng_pthread_mutex_impl lock; // Mutual exclusion on next two variables + waiter *head_waiter; // List of threads waiting on this condition + waiter *tail_waiter; // Used to get FIFO, rather than LIFO, behavior for pthread_cond_signal + unsigned int broadcast_generation; // Used as sort of a separator on broadcasts + // saves having to walk the waiters list setting + // each one's "chosen_by_signal" flag while the condition is locked + + dng_pthread_cond_impl() : head_waiter(NULL), tail_waiter(NULL), broadcast_generation(0) { } + ~dng_pthread_cond_impl() { } ; + +// Non copyable +private: + dng_pthread_cond_impl &operator=(const dng_pthread_cond_impl &) { } + dng_pthread_cond_impl(const dng_pthread_cond_impl &) { } + +}; + +/*****************************************************************************/ + +namespace +{ + + struct ScopedLock + { + dng_pthread_mutex_impl *mutex; + + ScopedLock(dng_pthread_mutex_impl *arg) : mutex(arg) + { + mutex->Lock(); + } + ScopedLock(dng_pthread_mutex_impl &arg) : mutex(&arg) + { + mutex->Lock(); + } + ~ScopedLock() + { + mutex->Unlock(); + } + private: + ScopedLock &operator=(const ScopedLock &) { } + ScopedLock(const ScopedLock &) { } + }; + + dng_pthread_mutex_impl validationLock; + + void ValidateMutex(dng_pthread_mutex_t *mutex) + { + if (*mutex != DNG_PTHREAD_MUTEX_INITIALIZER) + return; + + ScopedLock lock(validationLock); + + if (*mutex == DNG_PTHREAD_MUTEX_INITIALIZER) + dng_pthread_mutex_init(mutex, NULL); + } + + void ValidateCond(dng_pthread_cond_t *cond) + { + if (*cond != DNG_PTHREAD_COND_INITIALIZER) + return; + + ScopedLock lock(validationLock); + + if (*cond == DNG_PTHREAD_COND_INITIALIZER) + dng_pthread_cond_init(cond, NULL); + } + + DWORD thread_wait_sema_TLS_index; + bool thread_wait_sema_inited = false; + dng_pthread_once_t once_thread_TLS = DNG_PTHREAD_ONCE_INIT; + + void init_thread_TLS() + { + thread_wait_sema_TLS_index = ::TlsAlloc(); + thread_wait_sema_inited = true; + } + + void finalize_thread_TLS() + { + if (thread_wait_sema_inited) + { + ::TlsFree(thread_wait_sema_TLS_index); + thread_wait_sema_inited = false; + } + } + + dng_pthread_mutex_impl primaryHandleMapLock; + + typedef std::map > ThreadMapType; + + // A map to make sure handles are freed and to allow returning a pointer sized result + // even on 64-bit Windows. + ThreadMapType primaryHandleMap; + + HANDLE GetThreadSemaphore() + { + dng_pthread_once(&once_thread_TLS, init_thread_TLS); + + HANDLE semaphore = ::TlsGetValue(thread_wait_sema_TLS_index); + if (semaphore == NULL) + { + semaphore = ::CreateSemaphore(NULL, 0, 1, NULL); + ::TlsSetValue(thread_wait_sema_TLS_index, semaphore); + } + + return semaphore; + } + + void FreeThreadSemaphore() + { + if (thread_wait_sema_inited) + { + HANDLE semaphore = (HANDLE)::TlsGetValue(thread_wait_sema_TLS_index); + + if (semaphore != NULL) + { + ::TlsSetValue(thread_wait_sema_TLS_index, NULL); + ::CloseHandle(semaphore); + } + } + } + + struct trampoline_args + { + void *(*func)(void *); + void *arg; + }; + + // This trampoline takes care of the return type being different + // between pthreads thread funcs and Windows C lib thread funcs + unsigned __stdcall trampoline(void *arg_arg) + { + trampoline_args *args_ptr = (trampoline_args *)arg_arg; + trampoline_args args = *args_ptr; + + delete args_ptr; + + GetThreadSemaphore(); + + void *result = args.func(args.arg); + + { + ScopedLock lockMap(primaryHandleMapLock); + + ThreadMapType::iterator iter = primaryHandleMap.find(pthread_self()); + if (iter != primaryHandleMap.end()) + *iter->second.second = result; + } + + FreeThreadSemaphore(); + + return S_OK; + } + +} + +/*****************************************************************************/ + +extern "C" { + +/*****************************************************************************/ + +struct dng_pthread_attr_impl + { + size_t stacksize; + }; + +/*****************************************************************************/ + +int dng_pthread_attr_init(pthread_attr_t *attr) + { + dng_pthread_attr_impl *newAttrs; + + newAttrs = new (std::nothrow) dng_pthread_attr_impl; + if (newAttrs == NULL) + return -1; // ENOMEM; + + newAttrs->stacksize = 0; + + *attr = newAttrs; + + return 0; + } + +/*****************************************************************************/ + +int dng_pthread_attr_destroy(pthread_attr_t *attr) + { + if (*attr == NULL) + return -1; // EINVAL + + delete *attr; + + *attr = NULL; + + return 0; + } + +/*****************************************************************************/ + +int dng_pthread_attr_setstacksize(dng_pthread_attr_t *attr, size_t stacksize) + { + if (attr == NULL || (*attr) == NULL) + return -1; // EINVAL + + (*attr)->stacksize = stacksize; + + return 0; + } + +/*****************************************************************************/ + +int dng_pthread_attr_getstacksize(const dng_pthread_attr_t *attr, size_t *stacksize) + { + if (attr == NULL || (*attr) == NULL || stacksize == NULL) + return -1; // EINVAL + + *stacksize = (*attr)->stacksize; + + return 0; + } + +/*****************************************************************************/ + +int dng_pthread_create(dng_pthread_t *thread, const pthread_attr_t *attrs, void * (*func)(void *), void *arg) +{ + try + { + uintptr_t result; + unsigned threadID; + std::auto_ptr args(new (std::nothrow) trampoline_args); + std::auto_ptr resultHolder(new (std::nothrow) (void *)); + + if (args.get() == NULL || resultHolder.get () == NULL) + return -1; // ENOMEM + + args->func = func; + args->arg = arg; + + size_t stacksize = 0; + + if (attrs != NULL) + dng_pthread_attr_getstacksize (attrs, &stacksize); + + { + ScopedLock lockMap(primaryHandleMapLock); + + result = _beginthreadex(NULL, (unsigned)stacksize, trampoline, args.get(), 0, &threadID); + if (result == NULL) + return -1; // ENOMEM + args.release(); + + std::pair > newMapEntry(threadID, + std::pair((HANDLE)result, resultHolder.get ())); + std::pair insertion = primaryHandleMap.insert(newMapEntry); + + // If there is a handle open on the thread, its ID should not be reused so assert that an insertion was made. + DNG_ASSERT(insertion.second, "pthread emulation logic error"); + } + + + resultHolder.release (); + + *thread = (dng_pthread_t)threadID; + return 0; + } + catch (const std::bad_alloc &) + { + return -1; + } +} + +/*****************************************************************************/ + +int dng_pthread_detach(dng_pthread_t thread) +{ + HANDLE primaryHandle; + void **resultHolder = NULL; + + { + ScopedLock lockMap(primaryHandleMapLock); + + ThreadMapType::iterator iter = primaryHandleMap.find(thread); + if (iter == primaryHandleMap.end()) + return -1; + + primaryHandle = iter->second.first; + + // A join is waiting on the thread. + if (primaryHandle == NULL) + return -1; + + resultHolder = iter->second.second; + + primaryHandleMap.erase(iter); + } + + delete resultHolder; + + if (!::CloseHandle(primaryHandle)) + return -1; + + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_join(dng_pthread_t thread, void **result) +{ + bool found = false; + HANDLE primaryHandle = NULL; + void **resultHolder = NULL; + + ThreadMapType::iterator iter; + + { + ScopedLock lockMap(primaryHandleMapLock); + + iter = primaryHandleMap.find(thread); + found = iter != primaryHandleMap.end(); + if (found) + { + primaryHandle = iter->second.first; + resultHolder = iter->second.second; + + // Set HANDLE to NULL to force any later join or detach to fail. + iter->second.first = NULL; + } + } + + // This case can happens when joining a thread not created with pthread_create, + // which is a bad idea, but it gets mapped to doing the join, but always returns NULL. + if (!found) + primaryHandle = ::OpenThread(SYNCHRONIZE|THREAD_QUERY_INFORMATION, FALSE, thread); + + if (primaryHandle == NULL) + return -1; + + DWORD err; + if (::WaitForSingleObject(primaryHandle, INFINITE) != WAIT_OBJECT_0) + { + err = ::GetLastError(); + return -1; + } + + { + ScopedLock lockMap(primaryHandleMapLock); + + if (iter != primaryHandleMap.end()) + primaryHandleMap.erase(iter); + } + + ::CloseHandle(primaryHandle); + if (result != NULL && resultHolder != NULL) + *result = *resultHolder; + + delete resultHolder; + + return 0; +} + +/*****************************************************************************/ + +dng_pthread_t dng_pthread_self() +{ + return (dng_pthread_t)::GetCurrentThreadId(); +} + +/*****************************************************************************/ + +void dng_pthread_exit(void *result) +{ + { + ScopedLock lockMap(primaryHandleMapLock); + + ThreadMapType::iterator iter = primaryHandleMap.find(pthread_self()); + if (iter != primaryHandleMap.end()) + *iter->second.second = result; + } + + FreeThreadSemaphore(); + + _endthreadex(S_OK); +} + +/*****************************************************************************/ + +int dng_pthread_mutex_init(dng_pthread_mutex_t *mutex, void * /* attrs */) +{ + dng_pthread_mutex_t result; + try { + result = new(dng_pthread_mutex_impl); + } catch (const std::bad_alloc &) + { + return -1; + } + + if (result == NULL) + return -1; + *mutex = result; + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_mutex_destroy(dng_pthread_mutex_t *mutex) +{ + if (*mutex == DNG_PTHREAD_MUTEX_INITIALIZER) + { + *mutex = NULL; + return 0; + } + + delete *mutex; + *mutex = NULL; + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_cond_init(dng_pthread_cond_t *cond, void * /* attrs */) +{ + dng_pthread_cond_t result; + try { + result = new(dng_pthread_cond_impl); + } catch (const std::bad_alloc &) + { + return -1; + } + + if (result == NULL) + return -1; + *cond = result; + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_cond_destroy(dng_pthread_cond_t *cond) +{ + if (*cond == DNG_PTHREAD_COND_INITIALIZER) + { + *cond = NULL; + return 0; + } + + delete *cond; + *cond = NULL; + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_mutexattr_init(dng_pthread_mutexattr_t* mutexattr) +{ + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_mutexattr_settype(dng_pthread_mutexattr_t* mutexattr, int type) +{ + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_mutex_lock(dng_pthread_mutex_t *mutex) +{ + ValidateMutex(mutex); + (*mutex)->Lock(); + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_mutex_unlock(dng_pthread_mutex_t *mutex) +{ + ValidateMutex(mutex); + (*mutex)->Unlock(); + return 0; +} + +/*****************************************************************************/ + +static int cond_wait_internal(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex, int timeout_milliseconds) +{ + dng_pthread_cond_impl &real_cond = **cond; + dng_pthread_mutex_impl &real_mutex = **mutex; + + waiter this_wait; + HANDLE semaphore = GetThreadSemaphore(); + int my_generation; // The broadcast generation this waiter is in + + { + this_wait.next = NULL; + this_wait.semaphore = semaphore; + this_wait.chosen_by_signal = 0; + + ScopedLock lock1(real_cond.lock); + + // Add this waiter to the end of the list. + this_wait.prev = real_cond.tail_waiter; + if (real_cond.tail_waiter != NULL) + real_cond.tail_waiter->next = &this_wait; + real_cond.tail_waiter = &this_wait; + + // If the list was empty, set the head of the list to this waiter. + if (real_cond.head_waiter == NULL) + real_cond.head_waiter = &this_wait; + + // Note which broadcast generation this waiter belongs to. + my_generation = real_cond.broadcast_generation; + } + + real_mutex.Unlock(); + + DWORD result = ::WaitForSingleObject(semaphore, timeout_milliseconds); + + if (result == WAIT_TIMEOUT) + { + // If the wait timed out, this thread is likely still on the waiters list + // of the condition. However, there is a race in that the thread may have been + // signaled or broadcast between when WaitForSingleObject decided + // we had timed out and this code running. + + bool mustConsumeSemaphore = false; + { + ScopedLock lock2(real_cond.lock); + + bool chosen_by_signal = this_wait.chosen_by_signal; + bool chosen_by_broadcast = my_generation != real_cond.broadcast_generation; + + if (chosen_by_signal || chosen_by_broadcast) + mustConsumeSemaphore = true; + else + { + // Still on waiters list. Remove this waiter from list. + if (this_wait.next != NULL) + this_wait.next->prev = this_wait.prev; + else + real_cond.tail_waiter = this_wait.prev; + + if (this_wait.prev != NULL) + this_wait.prev->next = this_wait.next; + else + real_cond.head_waiter = this_wait.next; + } + } + + if (mustConsumeSemaphore) + { + ::WaitForSingleObject(semaphore, INFINITE); + result = WAIT_OBJECT_0; + } + } + else + DNG_ASSERT (result == WAIT_OBJECT_0, "pthread emulation logic error"); + + // reacquire the mutex + real_mutex.Lock(); + + return (result == WAIT_TIMEOUT) ? DNG_ETIMEDOUT : 0; +} + +/*****************************************************************************/ + +int dng_pthread_cond_wait(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex) +{ + ValidateCond(cond); + + return cond_wait_internal(cond, mutex, INFINITE); +} + +/*****************************************************************************/ + +int dng_pthread_cond_timedwait(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex, timespec *latest_time) +{ + ValidateCond(cond); + + struct timespec sys_timespec; + + dng_pthread_now (&sys_timespec); + + __int64 sys_time = (__int64)sys_timespec.tv_sec * 1000000000 + sys_timespec.tv_nsec; + __int64 lock_time = (__int64)latest_time->tv_sec * 1000000000 + latest_time->tv_nsec; + + int wait_millisecs = (int)((lock_time - sys_time + 500000) / 1000000); + + if (wait_millisecs < 0) + wait_millisecs = 0; + + return cond_wait_internal(cond, mutex, wait_millisecs); +} + +/*****************************************************************************/ + +int dng_pthread_cond_signal(dng_pthread_cond_t *cond) +{ + ValidateCond(cond); + + waiter *first; + dng_pthread_cond_impl &real_cond = **cond; + + { + ScopedLock lock(real_cond.lock); + + first = real_cond.head_waiter; + if (first != NULL) + { + if (first->next != NULL) + first->next->prev = NULL; + else + real_cond.tail_waiter = NULL; // Or first->prev, which is always NULL in this case + + first->chosen_by_signal = true; + + real_cond.head_waiter = first->next; + } + } + + if (first != NULL) + ::ReleaseSemaphore(first->semaphore, 1, NULL); + + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_cond_broadcast(dng_pthread_cond_t *cond) +{ + ValidateCond(cond); + + waiter *first; + dng_pthread_cond_impl &real_cond = **cond; + + { + ScopedLock lock(real_cond.lock); + + first = real_cond.head_waiter; + real_cond.head_waiter = NULL; + real_cond.tail_waiter = NULL; + + real_cond.broadcast_generation++; + } + + while (first != NULL) + { + waiter *next = first->next; + ::ReleaseSemaphore(first->semaphore, 1, NULL); + first = next; + } + + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_once(dng_pthread_once_t *once, void (*init_func)()) +{ + if (once == NULL || init_func == NULL) + return EINVAL; + + if (once->inited) + return 0; + + if (::InterlockedIncrement(&once->semaphore) == 0) + { + init_func(); + once->inited = 1; + } + else + { + while (!once->inited) + Sleep(0); + } + + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_key_create(dng_pthread_key_t * key, void (*destructor) (void *)) +{ + if (destructor != NULL) + return -1; + + DWORD result = ::TlsAlloc(); + if (result == TLS_OUT_OF_INDEXES) + return -1; + *key = (unsigned long)result; + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_key_delete(dng_pthread_key_t key) +{ + if (::TlsFree((DWORD)key)) + return 0; + return -1; +} + +/*****************************************************************************/ + +int dng_pthread_setspecific(dng_pthread_key_t key, const void *value) +{ + if (::TlsSetValue((DWORD)key, const_cast(value))) + return 0; + return -1; +} + +/*****************************************************************************/ + +void *dng_pthread_getspecific(dng_pthread_key_t key) +{ + return ::TlsGetValue((DWORD)key); +} + +/*****************************************************************************/ + +namespace { + struct rw_waiter { + struct rw_waiter *prev; + struct rw_waiter *next; + HANDLE semaphore; + bool is_writer; + }; +} + +struct dng_pthread_rwlock_impl +{ + dng_pthread_mutex_impl mutex; + + rw_waiter *head_waiter; + rw_waiter *tail_waiter; + + unsigned long readers_active; + unsigned long writers_waiting; + bool writer_active; + + dng_pthread_cond_impl read_wait; + dng_pthread_cond_impl write_wait; + + dng_pthread_rwlock_impl () + : mutex () + , head_waiter (NULL) + , tail_waiter (NULL) + , readers_active (0) + , writers_waiting (0) + , read_wait () + , write_wait () + , writer_active (false) + { + } + + ~dng_pthread_rwlock_impl () + { + } + + void WakeHeadWaiter () + { + HANDLE semaphore = head_waiter->semaphore; + + head_waiter = head_waiter->next; + if (head_waiter == NULL) + tail_waiter = NULL; + + ::ReleaseSemaphore(semaphore, 1, NULL); + } + +}; + +/*****************************************************************************/ + +int dng_pthread_rwlock_init(dng_pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attrs) +{ + dng_pthread_rwlock_impl *newRWLock; + + newRWLock = new (std::nothrow) dng_pthread_rwlock_impl; + if (newRWLock == NULL) + return -1; // ENOMEM; + + *rwlock = newRWLock; + + return 0; +} + +/*****************************************************************************/ + +int dng_pthread_rwlock_destroy(dng_pthread_rwlock_t *rwlock) +{ + dng_pthread_rwlock_impl &real_rwlock = **rwlock; + + { + ScopedLock lock (real_rwlock.mutex); + + if (real_rwlock.head_waiter != NULL || + real_rwlock.readers_active != 0 || + real_rwlock.writers_waiting != 0 || + real_rwlock.writer_active) + return -1; // EBUSY + } + + delete *rwlock; + *rwlock = NULL; + return 0; +} + +/*****************************************************************************/ + +#define CHECK_RWLOCK_STATE(real_rwlock) \ + DNG_ASSERT (!real_rwlock.writer_active || real_rwlock.readers_active == 0, "dng_pthread_rwlock_t logic error") + +/*****************************************************************************/ + +int dng_pthread_rwlock_rdlock(dng_pthread_rwlock_t *rwlock) +{ + dng_pthread_rwlock_impl &real_rwlock = **rwlock; + + struct rw_waiter this_wait; + bool doWait = false;; + int result = 0; + HANDLE semaphore=NULL; + + { + + ScopedLock lock (real_rwlock.mutex); + + CHECK_RWLOCK_STATE (real_rwlock); + + if (real_rwlock.writers_waiting > 0 || real_rwlock.writer_active) + { + semaphore = GetThreadSemaphore(); + + this_wait.next = NULL; + this_wait.semaphore = semaphore; + this_wait.is_writer = false; + + // Add this waiter to the end of the list. + this_wait.prev = real_rwlock.tail_waiter; + if (real_rwlock.tail_waiter != NULL) + real_rwlock.tail_waiter->next = &this_wait; + real_rwlock.tail_waiter = &this_wait; + + // If the list was empty, set the head of the list to this waiter. + if (real_rwlock.head_waiter == NULL) + real_rwlock.head_waiter = &this_wait; + + doWait = true; + } + else + real_rwlock.readers_active++; + } + + if (result == 0 && doWait) + result = (WaitForSingleObject(semaphore, INFINITE) == WAIT_OBJECT_0) ? 0 : -1; + + return result; +} + +/*****************************************************************************/ + +int dng_pthread_rwlock_tryrdlock(dng_pthread_rwlock_t *rwlock) +{ + dng_pthread_rwlock_impl &real_rwlock = **rwlock; + + ScopedLock lock (real_rwlock.mutex); + + CHECK_RWLOCK_STATE (real_rwlock); + + if (real_rwlock.writers_waiting == 0 && !real_rwlock.writer_active) + { + real_rwlock.readers_active++; + return 0; + } + + return -1; +} + +/*****************************************************************************/ + +int dng_pthread_rwlock_trywrlock(dng_pthread_rwlock_t *rwlock) +{ + dng_pthread_rwlock_impl &real_rwlock = **rwlock; + + ScopedLock lock (real_rwlock.mutex); + + CHECK_RWLOCK_STATE (real_rwlock); + + if (real_rwlock.readers_active == 0 && + real_rwlock.writers_waiting == 0 && + !real_rwlock.writer_active) + { + real_rwlock.writer_active = true; + return 0; + } + + return -1; +} + +/*****************************************************************************/ + +int dng_pthread_rwlock_unlock(dng_pthread_rwlock_t *rwlock) + { + dng_pthread_rwlock_impl &real_rwlock = **rwlock; + + int result = 0; + + ScopedLock lock (real_rwlock.mutex); + + CHECK_RWLOCK_STATE (real_rwlock); + + if (real_rwlock.readers_active > 0) + --real_rwlock.readers_active; + else + real_rwlock.writer_active = false; + + while (real_rwlock.head_waiter != NULL) + { + if (real_rwlock.head_waiter->is_writer) + { + if (real_rwlock.readers_active == 0) + { + real_rwlock.writers_waiting--; + real_rwlock.writer_active = true; + real_rwlock.WakeHeadWaiter (); + } + + break; + } + else + { + ++real_rwlock.readers_active; + real_rwlock.WakeHeadWaiter (); + } + } + + return result; + } + +/*****************************************************************************/ + +int dng_pthread_rwlock_wrlock(dng_pthread_rwlock_t *rwlock) + { + dng_pthread_rwlock_impl &real_rwlock = **rwlock; + + int result = 0; + struct rw_waiter this_wait; + HANDLE semaphore=NULL; + bool doWait = false; + + { + ScopedLock lock (real_rwlock.mutex); + + CHECK_RWLOCK_STATE (real_rwlock); + + if (real_rwlock.readers_active || + real_rwlock.writers_waiting || + real_rwlock.writer_active) + { + semaphore = GetThreadSemaphore(); + + this_wait.next = NULL; + this_wait.semaphore = semaphore; + this_wait.is_writer = true; + + // Add this waiter to the end of the list. + this_wait.prev = real_rwlock.tail_waiter; + if (real_rwlock.tail_waiter != NULL) + real_rwlock.tail_waiter->next = &this_wait; + real_rwlock.tail_waiter = &this_wait; + + // If the list was empty, set the head of the list to this waiter. + if (real_rwlock.head_waiter == NULL) + real_rwlock.head_waiter = &this_wait; + + real_rwlock.writers_waiting++; + + doWait = true; + } + else + real_rwlock.writer_active = true; + } + + if (result == 0 && doWait) + result = (WaitForSingleObject(semaphore, INFINITE) == WAIT_OBJECT_0) ? 0 : -1; + + return result; + } + +/*****************************************************************************/ + +void dng_pthread_disassociate() +{ + FreeThreadSemaphore(); +} + +void dng_pthread_terminate() + { + finalize_thread_TLS(); + } + +/*****************************************************************************/ + +} // extern "C" + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +int dng_pthread_now (struct timespec *now) + { + + if (now == NULL) + return -1; // EINVAL + + #if qWinOS + + FILETIME ft; + ::GetSystemTimeAsFileTime(&ft); + + __int64 sys_time = ((__int64)ft.dwHighDateTime << 32) + ft.dwLowDateTime; + + #define SecsFrom1601To1970 11644473600 + + sys_time -= SecsFrom1601To1970 * 10000000LL; + + sys_time *= 100; // Convert from 100ns to 1ns units + + now->tv_sec = (long)(sys_time / 1000000000); + now->tv_nsec = (long)(sys_time % 1000000000); + + #else + + struct timeval tv; + + if (gettimeofday (&tv, NULL) != 0) + return errno; + + now->tv_sec = tv.tv_sec; + now->tv_nsec = tv.tv_usec * 1000; + + #endif + + return 0; + + } + +/*****************************************************************************/ + +#endif // qDNGThreadSafe + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_pthread.h b/source/lib/dng_sdk/dng_pthread.h new file mode 100644 index 0000000..21958c5 --- /dev/null +++ b/source/lib/dng_sdk/dng_pthread.h @@ -0,0 +1,267 @@ +/*****************************************************************************/ +// Copyright 2002-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_pthread.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_pthread__ +#define __dng_pthread__ + +/*****************************************************************************/ + +#include "dng_flags.h" + +/*****************************************************************************/ + +#if qDNGThreadSafe + +/*****************************************************************************/ + +#if !qWinOS + +/*****************************************************************************/ + +/* Try generic POSIX compile */ + +#include +#include + +#define dng_pthread_disassociate() +#define dng_pthread_terminate() + +/*****************************************************************************/ + +#else + +/*****************************************************************************/ + +#include +#include + +#if _MSC_VER >= 1600 + +// Get this included so ETIMEDOUT is predefined. +#include + +#endif + +#ifdef __cplusplus +extern "C" +{ +#endif + +/*****************************************************************************/ + +#define DNG_ETIMEDOUT 60 /* Operation timed out */ + +struct dng_timespec { + long tv_sec; + long tv_nsec; +}; + +#if !defined(_MSC_VER) || _MSC_VER < 1900 +#define timespec dng_timespec +#endif +typedef unsigned long dng_pthread_t; + +typedef struct dng_pthread_mutex_impl *dng_pthread_mutex_t; +typedef struct dng_pthread_cond_impl *dng_pthread_cond_t; +typedef unsigned long dng_pthread_key_t; + + +#define DNG_PTHREAD_MUTEX_INITIALIZER ((struct dng_pthread_mutex_impl *)-1) +#define DNG_PTHREAD_COND_INITIALIZER ((struct dng_pthread_cond_impl *)-1) + +struct _dng_pthread_once_t { + int inited; + long semaphore; +}; + +typedef struct _dng_pthread_once_t dng_pthread_once_t; +#define DNG_PTHREAD_ONCE_INIT { 0, -1 } + +#define dng_pthread_equal(t1, t2) ((t1) == (t2)) + +typedef struct dng_pthread_attr_impl *dng_pthread_attr_t; + +int dng_pthread_attr_init(dng_pthread_attr_t *attr); +int dng_pthread_attr_destroy(dng_pthread_attr_t *attr); + +int dng_pthread_attr_setstacksize(dng_pthread_attr_t *attr, size_t stacksize); +int dng_pthread_attr_getstacksize(const dng_pthread_attr_t *attr, size_t *stacksize); + +int dng_pthread_create(dng_pthread_t *thread, const dng_pthread_attr_t * /* attrs */, void * (*func)(void *), void *arg); +int dng_pthread_detach(dng_pthread_t thread); +int dng_pthread_join(dng_pthread_t thread, void **result); +dng_pthread_t dng_pthread_self(); +void dng_pthread_exit(void *result); + +#define DNG_PTHREAD_MUTEX_RECURSIVE 0 +typedef unsigned long dng_pthread_mutexattr_t; + +int dng_pthread_mutexattr_init(dng_pthread_mutexattr_t *mutexattr); +int dng_pthread_mutexattr_settype(dng_pthread_mutexattr_t *mutexattr, int /*the options*/); + +int dng_pthread_mutex_init(dng_pthread_mutex_t *mutex, void * /* attrs */); +int dng_pthread_mutex_destroy(dng_pthread_mutex_t *mutex); +int dng_pthread_mutex_lock(dng_pthread_mutex_t *mutex); +int dng_pthread_mutex_unlock(dng_pthread_mutex_t *mutex); + +int dng_pthread_cond_init(dng_pthread_cond_t *cond, void * /* attrs */); +int dng_pthread_cond_destroy(dng_pthread_cond_t *cond); +int dng_pthread_cond_wait(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex); +int dng_pthread_cond_timedwait(dng_pthread_cond_t *cond, dng_pthread_mutex_t *mutex, timespec *latest_time); +int dng_pthread_cond_signal(dng_pthread_cond_t *cond); +int dng_pthread_cond_broadcast(dng_pthread_cond_t *cond); + +int dng_pthread_once(dng_pthread_once_t *once, void (*init_func)()); + +int dng_pthread_key_create(dng_pthread_key_t * key, void (*destructor) (void *)); +int dng_pthread_key_delete(dng_pthread_key_t key); +int dng_pthread_setspecific(dng_pthread_key_t key, const void *value); +void *dng_pthread_getspecific(dng_pthread_key_t key); + +typedef struct dng_pthread_rwlock_impl *dng_pthread_rwlock_t; +typedef void *pthread_rwlockattr_t; + +int dng_pthread_rwlock_destroy(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_init(dng_pthread_rwlock_t * rwlock, const pthread_rwlockattr_t * attrs); +int dng_pthread_rwlock_rdlock(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_tryrdlock(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_trywrlock(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_unlock(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_wrlock(dng_pthread_rwlock_t * rwlock); + +typedef struct dng_pthread_rwlock_impl *dng_pthread_rwlock_t; +typedef void *pthread_rwlockattr_t; + +int dng_pthread_rwlock_destroy(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_init(dng_pthread_rwlock_t * rwlock, const pthread_rwlockattr_t * attrs); +int dng_pthread_rwlock_rdlock(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_tryrdlock(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_trywrlock(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_unlock(dng_pthread_rwlock_t * rwlock); +int dng_pthread_rwlock_wrlock(dng_pthread_rwlock_t * rwlock); + +// dng_pthread may maintain per-thread global state. This routine frees that global state. +// there is no need to call this for threads created by dng_pthread and one can call +// dng_pthread routines of a thread after dng_pthread_disassociate as the global state will +// be recreated as necessary. However dng_pthread_disassociate will need to be called again +// and there is a slight performance cost. Do not call this routine while holding a mutex, etc. +void dng_pthread_disassociate(); + +void dng_pthread_terminate(); + +/*****************************************************************************/ + +// Map symbols back to plain pthread names. This whole mechanism is so the DNG pthreads library +// symbols do not collide with another pthread emulation library +// that may be in use in the same linked entity. However if that is the case, it would be far better +// to have the DNG code use the same pthread library as the rest of the code. + +#define pthread_t dng_pthread_t +#define pthread_mutex_t dng_pthread_mutex_t +#define pthread_cond_t dng_pthread_cond_t +#define pthread_once_t dng_pthread_once_t +#define pthread_key_t dng_pthread_key_t + +#undef PTHREAD_MUTEX_INITIALIZER +#define PTHREAD_MUTEX_INITIALIZER DNG_PTHREAD_MUTEX_INITIALIZER +#undef PTHREAD_COND_INITIALIZER +#define PTHREAD_COND_INITIALIZER DNG_PTHREAD_COND_INITIALIZER + +#undef PTHREAD_ONCE_INIT +#define PTHREAD_ONCE_INIT DNG_PTHREAD_ONCE_INIT + +/* If it is defined on Windows, it probably has the wrong value... */ +#if defined(WIN32) || !defined(ETIMEDOUT) +#undef ETIMEDOUT +#define ETIMEDOUT DNG_ETIMEDOUT +#endif + +#define pthread_equal dng_pthread_equal + +#define pthread_attr_t dng_pthread_attr_t + +#define pthread_attr_init dng_pthread_attr_init +#define pthread_attr_destroy dng_pthread_attr_destroy + +#define pthread_attr_setstacksize dng_pthread_attr_setstacksize +#define pthread_attr_getstacksize dng_pthread_attr_getstacksize + +#define pthread_create dng_pthread_create +#define pthread_detach dng_pthread_detach +#define pthread_join dng_pthread_join +#define pthread_self dng_pthread_self +#define pthread_exit dng_pthread_exit + +#define pthread_mutex_init dng_pthread_mutex_init +#define pthread_mutex_destroy dng_pthread_mutex_destroy +#define pthread_mutex_lock dng_pthread_mutex_lock +#define pthread_mutex_unlock dng_pthread_mutex_unlock + +#define pthread_cond_init dng_pthread_cond_init +#define pthread_cond_destroy dng_pthread_cond_destroy +#define pthread_cond_wait dng_pthread_cond_wait +#define pthread_cond_timedwait dng_pthread_cond_timedwait +#define pthread_cond_signal dng_pthread_cond_signal +#define pthread_cond_broadcast dng_pthread_cond_broadcast + +#define pthread_once dng_pthread_once + +#define pthread_key_create dng_pthread_key_create +#define pthread_key_delete dng_pthread_key_delete +#define pthread_setspecific dng_pthread_setspecific +#define pthread_getspecific dng_pthread_getspecific + +#define pthread_rwlock_t dng_pthread_rwlock_t + +#define pthread_rwlock_destroy dng_pthread_rwlock_destroy +#define pthread_rwlock_init dng_pthread_rwlock_init +#define pthread_rwlock_rdlock dng_pthread_rwlock_rdlock +#define pthread_rwlock_tryrdlock dng_pthread_rwlock_tryrdlock +#define pthread_rwlock_trywrlock dng_pthread_rwlock_trywrlock +#define pthread_rwlock_unlock dng_pthread_rwlock_unlock +#define pthread_rwlock_wrlock dng_pthread_rwlock_wrlock + +/*****************************************************************************/ + +#ifdef __cplusplus +} +#endif + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +#ifdef __cplusplus +extern "C" +{ +#endif + +int dng_pthread_now (struct timespec *now); + +#ifdef __cplusplus +} +#endif + +/*****************************************************************************/ + +#endif // qDNGThreadSafe + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_rational.cpp b/source/lib/dng_sdk/dng_rational.cpp new file mode 100644 index 0000000..c366b46 --- /dev/null +++ b/source/lib/dng_sdk/dng_rational.cpp @@ -0,0 +1,150 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_rational.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_rational.h" + +#include "dng_utils.h" + +/*****************************************************************************/ + +real64 dng_srational::As_real64 () const + { + + if (d) + return (real64) n / (real64) d; + + else + return 0.0; + + } + +/*****************************************************************************/ + +void dng_srational::Set_real64 (real64 x, int32 dd) + { + + if (x == 0.0) + { + + *this = dng_srational (0, 1); + + } + + if (dd == 0) + { + + real64 y = Abs_real64 (x); + + if (y >= 32768.0) + { + dd = 1; + } + + else if (y >= 1.0) + { + dd = 32768; + } + + else + { + dd = 32768 * 32768; + } + + } + + *this = dng_srational (Round_int32 (x * dd), dd); + + } + +/*****************************************************************************/ + +void dng_srational::ReduceByFactor (int32 factor) + { + + while (n % factor == 0 && + d % factor == 0 && + d >= factor) + { + n /= factor; + d /= factor; + } + + } + +/*****************************************************************************/ + +real64 dng_urational::As_real64 () const + { + + if (d) + return (real64) n / (real64) d; + + else + return 0.0; + + } + +/*****************************************************************************/ + +void dng_urational::Set_real64 (real64 x, uint32 dd) + { + + if (x <= 0.0) + { + + *this = dng_urational (0, 1); + + } + + if (dd == 0) + { + + if (x >= 32768.0) + { + dd = 1; + } + + else if (x >= 1.0) + { + dd = 32768; + } + + else + { + dd = 32768 * 32768; + } + + } + + *this = dng_urational (Round_uint32 (x * dd), dd); + + } + +/*****************************************************************************/ + +void dng_urational::ReduceByFactor (uint32 factor) + { + + while (n % factor == 0 && + d % factor == 0 && + d >= factor) + { + n /= factor; + d /= factor; + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_rational.h b/source/lib/dng_sdk/dng_rational.h new file mode 100644 index 0000000..9080a63 --- /dev/null +++ b/source/lib/dng_sdk/dng_rational.h @@ -0,0 +1,149 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_rational.h#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * Signed and unsigned rational data types. + */ + +/*****************************************************************************/ + +#ifndef __dng_rational__ +#define __dng_rational__ + +/*****************************************************************************/ + +#include "dng_types.h" + +/*****************************************************************************/ + +class dng_srational + { + + public: + + int32 n; // Numerator + int32 d; // Denominator + + public: + + dng_srational () + : n (0) + , d (0) + { + } + + dng_srational (int32 nn, int32 dd) + : n (nn) + , d (dd) + { + } + + void Clear () + { + n = 0; + d = 0; + } + + bool IsValid () const + { + return d != 0; + } + + bool NotValid () const + { + return !IsValid (); + } + + bool operator== (const dng_srational &r) const + { + return (n == r.n) && + (d == r.d); + } + + bool operator!= (const dng_srational &r) const + { + return !(*this == r); + } + + real64 As_real64 () const; + + void Set_real64 (real64 x, int32 dd = 0); + + void ReduceByFactor (int32 factor); + + }; + +/*****************************************************************************/ + +class dng_urational + { + + public: + + uint32 n; // Numerator + uint32 d; // Denominator + + public: + + dng_urational () + : n (0) + , d (0) + { + } + + dng_urational (uint32 nn, uint32 dd) + : n (nn) + , d (dd) + { + } + + void Clear () + { + n = 0; + d = 0; + } + + bool IsValid () const + { + return d != 0; + } + + bool NotValid () const + { + return !IsValid (); + } + + bool operator== (const dng_urational &r) const + { + return (n == r.n) && + (d == r.d); + } + + bool operator!= (const dng_urational &r) const + { + return !(*this == r); + } + + real64 As_real64 () const; + + void Set_real64 (real64 x, uint32 dd = 0); + + void ReduceByFactor (uint32 factor); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_read_image.cpp b/source/lib/dng_sdk/dng_read_image.cpp new file mode 100644 index 0000000..4b83d8c --- /dev/null +++ b/source/lib/dng_sdk/dng_read_image.cpp @@ -0,0 +1,3194 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_read_image.cpp#7 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_read_image.h" + +#include "dng_abort_sniffer.h" +#include "dng_area_task.h" +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_ifd.h" +#include "dng_jpeg_image.h" +#include "dng_lossless_jpeg.h" +#include "dng_mutex.h" +#include "dng_memory.h" +#include "dng_pixel_buffer.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +/******************************************************************************/ + +static void DecodeDelta8 (uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 channels) + { + + const uint32 dRowStep = cols * channels; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 1; col < cols; col++) + { + + for (uint32 channel = 0; channel < channels; channel++) + { + + dPtr [col * channels + channel] += dPtr [(col - 1) * channels + channel]; + + } + + } + + dPtr += dRowStep; + + } + + } + +/******************************************************************************/ + +static void DecodeDelta16 (uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 channels) + { + + const uint32 dRowStep = cols * channels; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 1; col < cols; col++) + { + + for (uint32 channel = 0; channel < channels; channel++) + { + + dPtr [col * channels + channel] += dPtr [(col - 1) * channels + channel]; + + } + + } + + dPtr += dRowStep; + + } + + } + +/******************************************************************************/ + +static void DecodeDelta32 (uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 channels) + { + + const uint32 dRowStep = cols * channels; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 1; col < cols; col++) + { + + for (uint32 channel = 0; channel < channels; channel++) + { + + dPtr [col * channels + channel] += dPtr [(col - 1) * channels + channel]; + + } + + } + + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +inline void DecodeDeltaBytes (uint8 *bytePtr, int32 cols, int32 channels) + { + + if (channels == 1) + { + + uint8 b0 = bytePtr [0]; + + bytePtr += 1; + + for (int32 col = 1; col < cols; ++col) + { + + b0 += bytePtr [0]; + + bytePtr [0] = b0; + + bytePtr += 1; + + } + + } + + else if (channels == 3) + { + + uint8 b0 = bytePtr [0]; + uint8 b1 = bytePtr [1]; + uint8 b2 = bytePtr [2]; + + bytePtr += 3; + + for (int32 col = 1; col < cols; ++col) + { + + b0 += bytePtr [0]; + b1 += bytePtr [1]; + b2 += bytePtr [2]; + + bytePtr [0] = b0; + bytePtr [1] = b1; + bytePtr [2] = b2; + + bytePtr += 3; + + } + + } + + else if (channels == 4) + { + + uint8 b0 = bytePtr [0]; + uint8 b1 = bytePtr [1]; + uint8 b2 = bytePtr [2]; + uint8 b3 = bytePtr [3]; + + bytePtr += 4; + + for (int32 col = 1; col < cols; ++col) + { + + b0 += bytePtr [0]; + b1 += bytePtr [1]; + b2 += bytePtr [2]; + b3 += bytePtr [3]; + + bytePtr [0] = b0; + bytePtr [1] = b1; + bytePtr [2] = b2; + bytePtr [3] = b3; + + bytePtr += 4; + + } + + } + + else + { + + for (int32 col = 1; col < cols; ++col) + { + + for (int32 chan = 0; chan < channels; ++chan) + { + + bytePtr [chan + channels] += bytePtr [chan]; + + } + + bytePtr += channels; + + } + + } + + } + +/*****************************************************************************/ + +static void DecodeFPDelta (uint8 *input, + uint8 *output, + int32 cols, + int32 channels, + int32 bytesPerSample) + { + + DecodeDeltaBytes (input, cols * bytesPerSample, channels); + + int32 rowIncrement = cols * channels; + + if (bytesPerSample == 2) + { + + #if qDNGBigEndian + const uint8 *input0 = input; + const uint8 *input1 = input + rowIncrement; + #else + const uint8 *input1 = input; + const uint8 *input0 = input + rowIncrement; + #endif + + for (int32 col = 0; col < rowIncrement; ++col) + { + + output [0] = input0 [col]; + output [1] = input1 [col]; + + output += 2; + + } + + } + + else if (bytesPerSample == 3) + { + + const uint8 *input0 = input; + const uint8 *input1 = input + rowIncrement; + const uint8 *input2 = input + rowIncrement * 2; + + for (int32 col = 0; col < rowIncrement; ++col) + { + + output [0] = input0 [col]; + output [1] = input1 [col]; + output [2] = input2 [col]; + + output += 3; + + } + + } + + else + { + + #if qDNGBigEndian + const uint8 *input0 = input; + const uint8 *input1 = input + rowIncrement; + const uint8 *input2 = input + rowIncrement * 2; + const uint8 *input3 = input + rowIncrement * 3; + #else + const uint8 *input3 = input; + const uint8 *input2 = input + rowIncrement; + const uint8 *input1 = input + rowIncrement * 2; + const uint8 *input0 = input + rowIncrement * 3; + #endif + + for (int32 col = 0; col < rowIncrement; ++col) + { + + output [0] = input0 [col]; + output [1] = input1 [col]; + output [2] = input2 [col]; + output [3] = input3 [col]; + + output += 4; + + } + + } + + } + +/*****************************************************************************/ + +bool DecodePackBits (dng_stream &stream, + uint8 *dPtr, + int32 dstCount) + { + + while (dstCount > 0) + { + + int32 runCount = (int8) stream.Get_uint8 (); + + if (runCount >= 0) + { + + ++runCount; + + dstCount -= runCount; + + if (dstCount < 0) + return false; + + stream.Get (dPtr, runCount); + + dPtr += runCount; + + } + + else + { + + runCount = -runCount + 1; + + dstCount -= runCount; + + if (dstCount < 0) + return false; + + uint8 x = stream.Get_uint8 (); + + while (runCount--) + { + + *(dPtr++) = x; + + } + + } + + } + + return true; + + } + +/******************************************************************************/ + +class dng_lzw_expander + { + + private: + + enum + { + kResetCode = 256, + kEndCode = 257, + kTableSize = 4096 + }; + + struct LZWExpanderNode + { + int16 prefix; + int16 final; + int16 depth; + int16 fake_for_padding; + }; + + dng_memory_data fBuffer; + + LZWExpanderNode *fTable; + + const uint8 *fSrcPtr; + + int32 fSrcCount; + + int32 fByteOffset; + + uint32 fBitBuffer; + int32 fBitBufferCount; + + int32 fNextCode; + + int32 fCodeSize; + + public: + + dng_lzw_expander (); + + bool Expand (const uint8 *sPtr, + uint8 *dPtr, + int32 sCount, + int32 dCount); + + private: + + void InitTable (); + + void AddTable (int32 w, int32 k); + + bool GetCodeWord (int32 &code); + + // Hidden copy constructor and assignment operator. + + dng_lzw_expander (const dng_lzw_expander &expander); + + dng_lzw_expander & operator= (const dng_lzw_expander &expander); + + }; + +/******************************************************************************/ + +dng_lzw_expander::dng_lzw_expander () + + : fBuffer () + , fTable (NULL) + , fSrcPtr (NULL) + , fSrcCount (0) + , fByteOffset (0) + , fBitBuffer (0) + , fBitBufferCount (0) + , fNextCode (0) + , fCodeSize (0) + + { + + fBuffer.Allocate (kTableSize * sizeof (LZWExpanderNode)); + + fTable = (LZWExpanderNode *) fBuffer.Buffer (); + + } + +/******************************************************************************/ + +void dng_lzw_expander::InitTable () + { + + fCodeSize = 9; + + fNextCode = 258; + + LZWExpanderNode *node = &fTable [0]; + + for (int32 code = 0; code < 256; code++) + { + + node->prefix = -1; + node->final = (int16) code; + node->depth = 1; + + node++; + + } + + } + +/******************************************************************************/ + +void dng_lzw_expander::AddTable (int32 w, int32 k) + { + + DNG_ASSERT ((w >= 0) && (w <= kTableSize), + "bad w value in dng_lzw_expander::AddTable"); + + LZWExpanderNode *parentNode = &fTable [w]; + + int32 nextCode = fNextCode; + + fNextCode++; + + DNG_ASSERT ((nextCode >= 0) && (nextCode <= kTableSize), + "bad fNextCode value in dng_lzw_expander::AddTable"); + + LZWExpanderNode *node = &fTable [nextCode]; + + node->prefix = (int16) w; + node->final = (int16) k; + node->depth = 1 + parentNode->depth; + + if (nextCode + 1 == (1 << fCodeSize) - 1) + { + if (fCodeSize != 12) + fCodeSize++; + } + + } + +/******************************************************************************/ + +bool dng_lzw_expander::GetCodeWord (int32 &code) + { + + // The bit buffer has the current code in the most significant bits, + // so shift off the low orders. + + int32 codeSize = fCodeSize; + + code = fBitBuffer >> (32 - codeSize); + + if (fBitBufferCount >= codeSize) + { + + // Typical case; get the code from the bit buffer. + + fBitBuffer <<= codeSize; + fBitBufferCount -= codeSize; + + } + + else + { + + // The buffer needs to be refreshed. + + const int32 bitsSoFar = fBitBufferCount; + + if (fByteOffset >= fSrcCount) + return false; + + // Buffer a long word + + const uint8 *ptr = fSrcPtr + fByteOffset; + + #if qDNGBigEndian + + fBitBuffer = *((const uint32 *) ptr); + + #else + + { + + uint32 b0 = ptr [0]; + uint32 b1 = ptr [1]; + uint32 b2 = ptr [2]; + uint32 b3 = ptr [3]; + + fBitBuffer = (((((b0 << 8) | b1) << 8) | b2) << 8) | b3; + + } + + #endif + + fBitBufferCount = 32; + + fByteOffset += 4; + + // Number of additional bits we need + + const int32 bitsUsed = codeSize - bitsSoFar; + + // Number of low order bits in the current buffer we don't care about + + const int32 bitsNotUsed = 32 - bitsUsed; + + code |= fBitBuffer >> bitsNotUsed; + + fBitBuffer <<= bitsUsed; + fBitBufferCount -= bitsUsed; + + } + + return true; + + } + +/******************************************************************************/ + +bool dng_lzw_expander::Expand (const uint8 *sPtr, + uint8 *dPtr, + int32 sCount, + int32 dCount) + { + + void *dStartPtr = dPtr; + + fSrcPtr = sPtr; + + fSrcCount = sCount; + + fByteOffset = 0; + + /* the master decode loop */ + + while (true) + { + + InitTable (); + + int32 code; + + do + { + + if (!GetCodeWord (code)) + return false; + + DNG_ASSERT (code <= fNextCode, + "Unexpected LZW code in dng_lzw_expander::Expand"); + + } + while (code == kResetCode); + + if (code == kEndCode) + return true; + + if (code > kEndCode) + return false; + + int32 oldCode = code; + int32 inChar = code; + + *(dPtr++) = (uint8) code; + + if (--dCount == 0) + return true; + + while (true) + { + + if (!GetCodeWord (code)) + return false; + + if (code == kResetCode) + break; + + if (code == kEndCode) + return true; + + const int32 inCode = code; + + bool repeatLastPixel = false; + + if (code >= fNextCode) + { + + // This is either a bad file or our code table is not big enough; we + // are going to repeat the last code seen and attempt to muddle thru. + + code = oldCode; + + repeatLastPixel = true; + + } + + // this can only happen if we hit 2 bad codes in a row + + if (code > fNextCode) + return false; + + const int32 depth = fTable [code].depth; + + if (depth < dCount) + { + + dCount -= depth; + + dPtr += depth; + + uint8 *ptr = dPtr; + + // give the compiler an extra hint to optimize these as registers + + const LZWExpanderNode *localTable = fTable; + + int32 localCode = code; + + // this is usually the hottest loop in LZW expansion + + while (localCode >= kResetCode) + { + + if (ptr <= dStartPtr) + return false; // about to trash memory + + const LZWExpanderNode &node = localTable [localCode]; + + uint8 tempFinal = (uint8) node.final; + + localCode = node.prefix; + + // Check for bogus table entry + + if (localCode < 0 || localCode > kTableSize) + return false; + + *(--ptr) = tempFinal; + + } + + code = localCode; + + inChar = localCode; + + if (ptr <= dStartPtr) + return false; // about to trash memory + + *(--ptr) = (uint8) inChar; + + } + + else + { + + // There might not be enough room for the full code + // so skip the end of it. + + const int32 skip = depth - dCount; + + for (int32 i = 0; i < skip ; i++) + { + const LZWExpanderNode &node = fTable [code]; + code = node.prefix; + } + + int32 depthUsed = depth - skip; + + dCount -= depthUsed; + + dPtr += depthUsed; + + uint8 *ptr = dPtr; + + while (code >= 0) + { + + if (ptr <= dStartPtr) + return false; // about to trash memory + + const LZWExpanderNode &node = fTable [code]; + + *(--ptr) = (uint8) node.final; + + code = node.prefix; + + // Check for bogus table entry + + if (code > kTableSize) + return false; + + } + + return true; + + } + + if (repeatLastPixel) + { + + *(dPtr++) = (uint8) inChar; + + if (--dCount == 0) + return true; + + } + + if (fNextCode < kTableSize) + { + + AddTable (oldCode, code); + + } + + oldCode = inCode; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +dng_row_interleaved_image::dng_row_interleaved_image (dng_image &image, + uint32 factor) + + : dng_image (image.Bounds (), + image.Planes (), + image.PixelType ()) + + , fImage (image ) + , fFactor (factor) + + { + + } + +/*****************************************************************************/ + +int32 dng_row_interleaved_image::MapRow (int32 row) const + { + + uint32 rows = Height (); + + int32 top = Bounds ().t; + + uint32 fieldRow = row - top; + + for (uint32 field = 0; true; field++) + { + + uint32 fieldRows = (rows - field + fFactor - 1) / fFactor; + + if (fieldRow < fieldRows) + { + + return fieldRow * fFactor + field + top; + + } + + fieldRow -= fieldRows; + + } + + ThrowProgramError (); + + return 0; + + } + +/*****************************************************************************/ + +void dng_row_interleaved_image::DoGet (dng_pixel_buffer &buffer) const + { + + dng_pixel_buffer tempBuffer (buffer); + + for (int32 row = buffer.fArea.t; row < buffer.fArea.b; row++) + { + + tempBuffer.fArea.t = MapRow (row); + + tempBuffer.fArea.b = tempBuffer.fArea.t + 1; + + tempBuffer.fData = (void *) buffer.DirtyPixel (row, + buffer.fArea.l, + buffer.fPlane); + + fImage.Get (tempBuffer); + + } + + } + +/*****************************************************************************/ + +void dng_row_interleaved_image::DoPut (const dng_pixel_buffer &buffer) + { + + dng_pixel_buffer tempBuffer (buffer); + + for (int32 row = buffer.fArea.t; row < buffer.fArea.b; row++) + { + + tempBuffer.fArea.t = MapRow (row); + + tempBuffer.fArea.b = tempBuffer.fArea.t + 1; + + tempBuffer.fData = (void *) buffer.ConstPixel (row, + buffer.fArea.l, + buffer.fPlane); + + fImage.Put (tempBuffer); + + } + + } + +/*****************************************************************************/ + +static void ReorderSubTileBlocks (dng_host &host, + const dng_ifd &ifd, + dng_pixel_buffer &buffer, + AutoPtr &tempBuffer) + { + + uint32 tempBufferSize = buffer.fArea.W () * + buffer.fArea.H () * + buffer.fPlanes * + buffer.fPixelSize; + + if (!tempBuffer.Get () || tempBuffer->LogicalSize () < tempBufferSize) + { + + tempBuffer.Reset (host.Allocate (tempBufferSize)); + + } + + uint32 blockRows = ifd.fSubTileBlockRows; + uint32 blockCols = ifd.fSubTileBlockCols; + + uint32 rowBlocks = buffer.fArea.H () / blockRows; + uint32 colBlocks = buffer.fArea.W () / blockCols; + + int32 rowStep = buffer.fRowStep * buffer.fPixelSize; + int32 colStep = buffer.fColStep * buffer.fPixelSize; + + int32 rowBlockStep = rowStep * blockRows; + int32 colBlockStep = colStep * blockCols; + + uint32 blockColBytes = blockCols * buffer.fPlanes * buffer.fPixelSize; + + const uint8 *s0 = (const uint8 *) buffer.fData; + uint8 *d0 = tempBuffer->Buffer_uint8 (); + + for (uint32 rowBlock = 0; rowBlock < rowBlocks; rowBlock++) + { + + uint8 *d1 = d0; + + for (uint32 colBlock = 0; colBlock < colBlocks; colBlock++) + { + + uint8 *d2 = d1; + + for (uint32 blockRow = 0; blockRow < blockRows; blockRow++) + { + + for (uint32 j = 0; j < blockColBytes; j++) + { + + d2 [j] = s0 [j]; + + } + + s0 += blockColBytes; + + d2 += rowStep; + + } + + d1 += colBlockStep; + + } + + d0 += rowBlockStep; + + } + + // Copy back reordered pixels. + + DoCopyBytes (tempBuffer->Buffer (), + buffer.fData, + tempBufferSize); + + } + +/*****************************************************************************/ + +class dng_image_spooler: public dng_spooler + { + + private: + + dng_host &fHost; + + const dng_ifd &fIFD; + + dng_image &fImage; + + dng_rect fTileArea; + + uint32 fPlane; + uint32 fPlanes; + + dng_memory_block &fBlock; + + AutoPtr &fSubTileBuffer; + + dng_rect fTileStrip; + + uint8 *fBuffer; + + uint32 fBufferCount; + uint32 fBufferSize; + + public: + + dng_image_spooler (dng_host &host, + const dng_ifd &ifd, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + dng_memory_block &block, + AutoPtr &subTileBuffer); + + virtual ~dng_image_spooler (); + + virtual void Spool (const void *data, + uint32 count); + + private: + + // Hidden copy constructor and assignment operator. + + dng_image_spooler (const dng_image_spooler &spooler); + + dng_image_spooler & operator= (const dng_image_spooler &spooler); + + }; + +/*****************************************************************************/ + +dng_image_spooler::dng_image_spooler (dng_host &host, + const dng_ifd &ifd, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + dng_memory_block &block, + AutoPtr &subTileBuffer) + + : fHost (host) + , fIFD (ifd) + , fImage (image) + , fTileArea (tileArea) + , fPlane (plane) + , fPlanes (planes) + , fBlock (block) + , fSubTileBuffer (subTileBuffer) + + , fTileStrip () + , fBuffer (NULL) + , fBufferCount (0) + , fBufferSize (0) + + { + + uint32 bytesPerRow = fTileArea.W () * fPlanes * (uint32) sizeof (uint16); + + uint32 stripLength = Pin_uint32 (ifd.fSubTileBlockRows, + fBlock.LogicalSize () / bytesPerRow, + fTileArea.H ()); + + stripLength = stripLength / ifd.fSubTileBlockRows + * ifd.fSubTileBlockRows; + + fTileStrip = fTileArea; + fTileStrip.b = fTileArea.t + stripLength; + + fBuffer = (uint8 *) fBlock.Buffer (); + + fBufferCount = 0; + fBufferSize = bytesPerRow * stripLength; + + } + +/*****************************************************************************/ + +dng_image_spooler::~dng_image_spooler () + { + + } + +/*****************************************************************************/ + +void dng_image_spooler::Spool (const void *data, + uint32 count) + { + + while (count) + { + + uint32 block = Min_uint32 (count, fBufferSize - fBufferCount); + + if (block == 0) + { + return; + } + + DoCopyBytes (data, + fBuffer + fBufferCount, + block); + + data = ((const uint8 *) data) + block; + + count -= block; + + fBufferCount += block; + + if (fBufferCount == fBufferSize) + { + + fHost.SniffForAbort (); + + dng_pixel_buffer buffer; + + buffer.fArea = fTileStrip; + + buffer.fPlane = fPlane; + buffer.fPlanes = fPlanes; + + buffer.fRowStep = fPlanes * fTileStrip.W (); + buffer.fColStep = fPlanes; + buffer.fPlaneStep = 1; + + buffer.fData = fBuffer; + + buffer.fPixelType = ttShort; + buffer.fPixelSize = 2; + + if (fIFD.fSubTileBlockRows > 1) + { + + ReorderSubTileBlocks (fHost, + fIFD, + buffer, + fSubTileBuffer); + + } + + fImage.Put (buffer); + + uint32 stripLength = fTileStrip.H (); + + fTileStrip.t = fTileStrip.b; + + fTileStrip.b = Min_int32 (fTileStrip.t + stripLength, + fTileArea.b); + + fBufferCount = 0; + + fBufferSize = fTileStrip.W () * + fTileStrip.H () * + fPlanes * (uint32) sizeof (uint16); + + } + + } + + } + +/*****************************************************************************/ + +dng_read_image::dng_read_image () + + : fJPEGTables () + + { + + } + +/*****************************************************************************/ + +dng_read_image::~dng_read_image () + { + + } + +/*****************************************************************************/ + +bool dng_read_image::ReadUncompressed (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer) + { + + uint32 rows = tileArea.H (); + uint32 samplesPerRow = tileArea.W (); + + if (ifd.fPlanarConfiguration == pcRowInterleaved) + { + rows *= planes; + } + else + { + samplesPerRow *= planes; + } + + uint32 samplesPerTile = samplesPerRow * rows; + + dng_pixel_buffer buffer; + + buffer.fArea = tileArea; + + buffer.fPlane = plane; + buffer.fPlanes = planes; + + buffer.fRowStep = planes * tileArea.W (); + + if (ifd.fPlanarConfiguration == pcRowInterleaved) + { + buffer.fColStep = 1; + buffer.fPlaneStep = tileArea.W (); + } + + else + { + buffer.fColStep = planes; + buffer.fPlaneStep = 1; + } + + if (uncompressedBuffer.Get () == NULL) + { + + #if qDNGValidate + + ReportError ("Fuzz: Missing uncompressed buffer"); + + #endif + + ThrowBadFormat (); + + } + + buffer.fData = uncompressedBuffer->Buffer (); + + uint32 bitDepth = ifd.fBitsPerSample [plane]; + + if (bitDepth == 8) + { + + buffer.fPixelType = ttByte; + buffer.fPixelSize = 1; + + stream.Get (buffer.fData, samplesPerTile); + + } + + else if (bitDepth == 16 && ifd.fSampleFormat [0] == sfFloatingPoint) + { + + buffer.fPixelType = ttFloat; + buffer.fPixelSize = 4; + + uint32 *p_uint32 = (uint32 *) buffer.fData; + + for (uint32 j = 0; j < samplesPerTile; j++) + { + + p_uint32 [j] = DNG_HalfToFloat (stream.Get_uint16 ()); + + } + + } + + else if (bitDepth == 24 && ifd.fSampleFormat [0] == sfFloatingPoint) + { + + buffer.fPixelType = ttFloat; + buffer.fPixelSize = 4; + + uint32 *p_uint32 = (uint32 *) buffer.fData; + + for (uint32 j = 0; j < samplesPerTile; j++) + { + + uint8 input [3]; + + if (stream.LittleEndian ()) + { + input [2] = stream.Get_uint8 (); + input [1] = stream.Get_uint8 (); + input [0] = stream.Get_uint8 (); + } + + else + { + input [0] = stream.Get_uint8 (); + input [1] = stream.Get_uint8 (); + input [2] = stream.Get_uint8 (); + } + + p_uint32 [j] = DNG_FP24ToFloat (input); + + } + + } + + else if (bitDepth == 16) + { + + buffer.fPixelType = ttShort; + buffer.fPixelSize = 2; + + stream.Get (buffer.fData, samplesPerTile * 2); + + if (stream.SwapBytes ()) + { + + DoSwapBytes16 ((uint16 *) buffer.fData, + samplesPerTile); + + } + + } + + else if (bitDepth == 32) + { + + buffer.fPixelType = image.PixelType (); + buffer.fPixelSize = 4; + + stream.Get (buffer.fData, samplesPerTile * 4); + + if (stream.SwapBytes ()) + { + + DoSwapBytes32 ((uint32 *) buffer.fData, + samplesPerTile); + + } + + } + + else if (bitDepth == 12) + { + + buffer.fPixelType = ttShort; + buffer.fPixelSize = 2; + + uint16 *p = (uint16 *) buffer.fData; + + uint32 evenSamples = samplesPerRow >> 1; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 j = 0; j < evenSamples; j++) + { + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + + p [0] = (uint16) ((b0 << 4) | (b1 >> 4)); + p [1] = (uint16) (((b1 << 8) | b2) & 0x0FFF); + + p += 2; + + } + + if (samplesPerRow & 1) + { + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + + p [0] = (uint16) ((b0 << 4) | (b1 >> 4)); + + p += 1; + + } + + } + + } + + else if (bitDepth > 8 && bitDepth < 16) + { + + buffer.fPixelType = ttShort; + buffer.fPixelSize = 2; + + uint16 *p = (uint16 *) buffer.fData; + + uint32 bitMask = (1 << bitDepth) - 1; + + for (uint32 row = 0; row < rows; row++) + { + + uint32 bitBuffer = 0; + uint32 bufferBits = 0; + + for (uint32 j = 0; j < samplesPerRow; j++) + { + + while (bufferBits < bitDepth) + { + + bitBuffer = (bitBuffer << 8) | stream.Get_uint8 (); + + bufferBits += 8; + + } + + p [j] = (uint16) ((bitBuffer >> (bufferBits - bitDepth)) & bitMask); + + bufferBits -= bitDepth; + + } + + p += samplesPerRow; + + } + + } + + else if (bitDepth > 16 && bitDepth < 32) + { + + buffer.fPixelType = ttLong; + buffer.fPixelSize = 4; + + uint32 *p = (uint32 *) buffer.fData; + + uint32 bitMask = (1 << bitDepth) - 1; + + for (uint32 row = 0; row < rows; row++) + { + + uint64 bitBuffer = 0; + uint32 bufferBits = 0; + + for (uint32 j = 0; j < samplesPerRow; j++) + { + + while (bufferBits < bitDepth) + { + + bitBuffer = (bitBuffer << 8) | stream.Get_uint8 (); + + bufferBits += 8; + + } + + p [j] = ((uint32) (bitBuffer >> (bufferBits - bitDepth))) & bitMask; + + bufferBits -= bitDepth; + + } + + p += samplesPerRow; + + } + + } + + else + { + + return false; + + } + + if (ifd.fSampleBitShift) + { + + buffer.ShiftRight (ifd.fSampleBitShift); + + } + + if (ifd.fSubTileBlockRows > 1) + { + + ReorderSubTileBlocks (host, + ifd, + buffer, + subTileBlockBuffer); + + } + + image.Put (buffer); + + return true; + + } + +/*****************************************************************************/ + +void dng_read_image::DecodeLossyJPEG (dng_host &host, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 /* photometricInterpretation */, + uint32 jpegDataSize, + uint8 *jpegDataInMemory) + { + + // The dng_sdk does not include a lossy JPEG decoder. Override this + // this method to add lossy JPEG support. + + (void) host; + (void) image; + (void) tileArea; + (void) plane; + (void) planes; + (void) jpegDataSize; + (void) jpegDataInMemory; + + ThrowProgramError ("Missing lossy JPEG decoder"); + + } + +/*****************************************************************************/ + +static dng_memory_block * ReadJPEGDataToBlock (dng_host &host, + dng_stream &stream, + dng_memory_block *tablesBlock, + uint64 tileOffset, + uint32 tileByteCount, + bool patchFirstByte) + { + + if (tileByteCount <= 2) + { + ThrowEndOfFile (); + } + + uint32 tablesByteCount = tablesBlock ? tablesBlock->LogicalSize () : 0; + + if (tablesByteCount && tablesByteCount < 4) + { + ThrowEndOfFile (); + } + + // The JPEG tables start with a two byte SOI marker, and + // and end with a two byte EOI marker. The JPEG tile + // data also starts with a two byte SOI marker. We can + // convert this combination a normal JPEG stream removing + // the last two bytes of the JPEG tables and the first two + // bytes of the tile data, and then concatenating them. + + if (tablesByteCount) + { + + tablesByteCount -= 2; + + tileOffset += 2; + tileByteCount -= 2; + + } + + // Allocate buffer. + + AutoPtr buffer (host.Allocate (tablesByteCount + tileByteCount)); + + // Read in table. + + if (tablesByteCount) + { + + DoCopyBytes (tablesBlock->Buffer (), + buffer->Buffer (), + tablesByteCount); + + } + + // Read in tile data. + + stream.SetReadPosition (tileOffset); + + stream.Get (buffer->Buffer_uint8 () + tablesByteCount, tileByteCount); + + // Patch first byte, if required. + + if (patchFirstByte) + { + + buffer->Buffer_uint8 () [0] = 0xFF; + + } + + // Return buffer. + + return buffer.Release (); + + } + +/*****************************************************************************/ + +bool dng_read_image::ReadBaselineJPEG (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + uint8 *jpegDataInMemory) + { + + // Setup the data source. + + if (fJPEGTables.Get () || !jpegDataInMemory) + { + + AutoPtr jpegDataBlock; + + jpegDataBlock.Reset (ReadJPEGDataToBlock (host, + stream, + fJPEGTables.Get (), + stream.Position (), + tileByteCount, + ifd.fPatchFirstJPEGByte)); + + DecodeLossyJPEG (host, + image, + tileArea, + plane, + planes, + ifd.fPhotometricInterpretation, + jpegDataBlock->LogicalSize (), + jpegDataBlock->Buffer_uint8 ()); + + } + + else + { + + if (ifd.fPatchFirstJPEGByte && tileByteCount) + { + jpegDataInMemory [0] = 0xFF; + } + + DecodeLossyJPEG (host, + image, + tileArea, + plane, + planes, + ifd.fPhotometricInterpretation, + tileByteCount, + jpegDataInMemory); + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_read_image::ReadLosslessJPEG (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer) + { + + uint32 bytesPerRow = tileArea.W () * planes * (uint32) sizeof (uint16); + + uint32 rowsPerStrip = Pin_uint32 (ifd.fSubTileBlockRows, + kImageBufferSize / bytesPerRow, + tileArea.H ()); + + rowsPerStrip = rowsPerStrip / ifd.fSubTileBlockRows + * ifd.fSubTileBlockRows; + + uint32 bufferSize = bytesPerRow * rowsPerStrip; + + if (uncompressedBuffer.Get () && + uncompressedBuffer->LogicalSize () < bufferSize) + { + + uncompressedBuffer.Reset (); + + } + + if (uncompressedBuffer.Get () == NULL) + { + + uncompressedBuffer.Reset (host.Allocate (bufferSize)); + + } + + dng_image_spooler spooler (host, + ifd, + image, + tileArea, + plane, + planes, + *uncompressedBuffer.Get (), + subTileBlockBuffer); + + uint32 decodedSize = tileArea.W () * + tileArea.H () * + planes * (uint32) sizeof (uint16); + + bool bug16 = ifd.fLosslessJPEGBug16; + + uint64 tileOffset = stream.Position (); + + DecodeLosslessJPEG (stream, + spooler, + decodedSize, + decodedSize, + bug16); + + if (stream.Position () > tileOffset + tileByteCount) + { + ThrowBadFormat (); + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_read_image::CanReadTile (const dng_ifd &ifd) + { + + if (ifd.fSampleFormat [0] != sfUnsignedInteger && + ifd.fSampleFormat [0] != sfFloatingPoint) + { + return false; + } + + switch (ifd.fCompression) + { + + case ccUncompressed: + { + + if (ifd.fSampleFormat [0] == sfFloatingPoint) + { + + return (ifd.fBitsPerSample [0] == 16 || + ifd.fBitsPerSample [0] == 24 || + ifd.fBitsPerSample [0] == 32); + + } + + return ifd.fBitsPerSample [0] >= 8 && + ifd.fBitsPerSample [0] <= 32; + + } + + case ccJPEG: + { + + if (ifd.fSampleFormat [0] != sfUnsignedInteger) + { + return false; + } + + if (ifd.IsBaselineJPEG ()) + { + + // Baseline JPEG. + + return true; + + } + + else + { + + // Lossless JPEG. + + return ifd.fBitsPerSample [0] >= 8 && + ifd.fBitsPerSample [0] <= 16; + + } + + break; + + } + + case ccLZW: + case ccDeflate: + case ccOldDeflate: + case ccPackBits: + { + + if (ifd.fSampleFormat [0] == sfFloatingPoint) + { + + if (ifd.fCompression == ccPackBits) + { + return false; + } + + if (ifd.fPredictor != cpNullPredictor && + ifd.fPredictor != cpFloatingPoint && + ifd.fPredictor != cpFloatingPointX2 && + ifd.fPredictor != cpFloatingPointX4) + { + return false; + } + + if (ifd.fBitsPerSample [0] != 16 && + ifd.fBitsPerSample [0] != 24 && + ifd.fBitsPerSample [0] != 32) + { + return false; + } + + } + + else + { + + if (ifd.fPredictor != cpNullPredictor && + ifd.fPredictor != cpHorizontalDifference && + ifd.fPredictor != cpHorizontalDifferenceX2 && + ifd.fPredictor != cpHorizontalDifferenceX4) + { + return false; + } + + if (ifd.fBitsPerSample [0] != 8 && + ifd.fBitsPerSample [0] != 16 && + ifd.fBitsPerSample [0] != 32) + { + return false; + } + + } + + return true; + + } + + default: + { + break; + } + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_read_image::NeedsCompressedBuffer (const dng_ifd &ifd) + { + + if (ifd.fCompression == ccLZW || + ifd.fCompression == ccDeflate || + ifd.fCompression == ccOldDeflate || + ifd.fCompression == ccPackBits) + { + return true; + } + + return false; + + } + +/*****************************************************************************/ + +void dng_read_image::ByteSwapBuffer (dng_host & /* host */, + dng_pixel_buffer &buffer) + { + + uint32 pixels = buffer.fRowStep * buffer.fArea.H (); + + switch (buffer.fPixelSize) + { + + case 2: + { + + DoSwapBytes16 ((uint16 *) buffer.fData, + pixels); + + break; + + } + + case 4: + { + + DoSwapBytes32 ((uint32 *) buffer.fData, + pixels); + + break; + + } + + default: + break; + + } + + } + +/*****************************************************************************/ + +void dng_read_image::DecodePredictor (dng_host & /* host */, + const dng_ifd &ifd, + dng_pixel_buffer &buffer) + { + + switch (ifd.fPredictor) + { + + case cpNullPredictor: + { + + return; + + } + + case cpHorizontalDifference: + case cpHorizontalDifferenceX2: + case cpHorizontalDifferenceX4: + { + + int32 xFactor = 1; + + if (ifd.fPredictor == cpHorizontalDifferenceX2) + { + xFactor = 2; + } + + else if (ifd.fPredictor == cpHorizontalDifferenceX4) + { + xFactor = 4; + } + + switch (buffer.fPixelType) + { + + case ttByte: + { + + DecodeDelta8 ((uint8 *) buffer.fData, + buffer.fArea.H (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor); + + return; + + } + + case ttShort: + { + + DecodeDelta16 ((uint16 *) buffer.fData, + buffer.fArea.H (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor); + + return; + + } + + case ttLong: + { + + DecodeDelta32 ((uint32 *) buffer.fData, + buffer.fArea.H (), + buffer.fArea.W () / xFactor, + buffer.fPlanes * xFactor); + + return; + + } + + default: + break; + + } + + break; + + } + + default: + break; + + } + + ThrowBadFormat (); + + } + +/*****************************************************************************/ + +void dng_read_image::ReadTile (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer) + { + + switch (ifd.fCompression) + { + case ccLZW: + case ccDeflate: + case ccOldDeflate: + case ccPackBits: + { + + // Figure out uncompressed size. + + uint32 bytesPerSample = (ifd.fBitsPerSample [0] >> 3); + + uint32 sampleCount = planes * tileArea.W () * tileArea.H (); + + uint32 uncompressedSize = sampleCount * bytesPerSample; + + // Setup pixel buffer to hold uncompressed data. + + dng_pixel_buffer buffer; + + if (ifd.fSampleFormat [0] == sfFloatingPoint) + { + buffer.fPixelType = ttFloat; + } + + else if (ifd.fBitsPerSample [0] == 8) + { + buffer.fPixelType = ttByte; + } + + else if (ifd.fBitsPerSample [0] == 16) + { + buffer.fPixelType = ttShort; + } + + else if (ifd.fBitsPerSample [0] == 32) + { + buffer.fPixelType = ttLong; + } + + else + { + ThrowBadFormat (); + } + + buffer.fPixelSize = bytesPerSample; + + buffer.fArea = tileArea; + + buffer.fPlane = plane; + buffer.fPlanes = planes; + + buffer.fPlaneStep = 1; + + buffer.fColStep = planes; + buffer.fRowStep = planes * tileArea.W (); + + uint32 bufferSize = uncompressedSize; + + // If we are using the floating point predictor, we need an extra + // buffer row. + + if (ifd.fPredictor == cpFloatingPoint || + ifd.fPredictor == cpFloatingPointX2 || + ifd.fPredictor == cpFloatingPointX4) + { + bufferSize += buffer.fRowStep * buffer.fPixelSize; + } + + // If are processing less than full size floating point data, + // we need space to expand the data to full floating point size. + + if (buffer.fPixelType == ttFloat) + { + bufferSize = Max_uint32 (bufferSize, sampleCount * 4); + } + + // Sometimes with multi-threading and planar image using strips, + // we can process a small tile before a large tile on a thread. + // Simple fix is to just reallocate the buffer if it is too small. + + if (uncompressedBuffer.Get () && + uncompressedBuffer->LogicalSize () < bufferSize) + { + + uncompressedBuffer.Reset (); + + } + + if (uncompressedBuffer.Get () == NULL) + { + + uncompressedBuffer.Reset (host.Allocate (bufferSize)); + + } + + buffer.fData = uncompressedBuffer->Buffer (); + + // If using floating point predictor, move buffer pointer to second row. + + if (ifd.fPredictor == cpFloatingPoint || + ifd.fPredictor == cpFloatingPointX2 || + ifd.fPredictor == cpFloatingPointX4) + { + + buffer.fData = (uint8 *) buffer.fData + + buffer.fRowStep * buffer.fPixelSize; + + } + + // Decompress the data. + + if (ifd.fCompression == ccLZW) + { + + dng_lzw_expander expander; + + if (!expander.Expand (compressedBuffer->Buffer_uint8 (), + (uint8 *) buffer.fData, + tileByteCount, + uncompressedSize)) + { + ThrowBadFormat (); + } + + } + + else if (ifd.fCompression == ccPackBits) + { + + dng_stream subStream (compressedBuffer->Buffer_uint8 (), + tileByteCount); + + if (!DecodePackBits (subStream, + (uint8 *) buffer.fData, + uncompressedSize)) + { + ThrowBadFormat (); + } + + } + + else + { + // Not supported currently + ThrowProgramError(); + } + + // The floating point predictor is byte order independent. + + if (ifd.fPredictor == cpFloatingPoint || + ifd.fPredictor == cpFloatingPointX2 || + ifd.fPredictor == cpFloatingPointX4) + { + + int32 xFactor = 1; + + if (ifd.fPredictor == cpFloatingPointX2) + { + xFactor = 2; + } + + else if (ifd.fPredictor == cpFloatingPointX4) + { + xFactor = 4; + } + + for (int32 row = tileArea.t; row < tileArea.b; row++) + { + + uint8 *srcPtr = (uint8 *) buffer.DirtyPixel (row , tileArea.l, plane); + uint8 *dstPtr = (uint8 *) buffer.DirtyPixel (row - 1, tileArea.l, plane); + + DecodeFPDelta (srcPtr, + dstPtr, + tileArea.W () / xFactor, + planes * xFactor, + bytesPerSample); + + } + + buffer.fData = (uint8 *) buffer.fData - + buffer.fRowStep * buffer.fPixelSize; + + } + + else + { + + // Both these compression algorithms are byte based. + + if (stream.SwapBytes ()) + { + + ByteSwapBuffer (host, + buffer); + + } + + // Undo the predictor. + + DecodePredictor (host, + ifd, + buffer); + + } + + // Expand floating point data, if needed. + + if (buffer.fPixelType == ttFloat && buffer.fPixelSize == 2) + { + + uint16 *srcPtr = (uint16 *) buffer.fData; + uint32 *dstPtr = (uint32 *) buffer.fData; + + for (int32 index = sampleCount - 1; index >= 0; index--) + { + + dstPtr [index] = DNG_HalfToFloat (srcPtr [index]); + + } + + buffer.fPixelSize = 4; + + } + + else if (buffer.fPixelType == ttFloat && buffer.fPixelSize == 3) + { + + uint8 *srcPtr = ((uint8 *) buffer.fData) + (sampleCount - 1) * 3; + uint32 *dstPtr = ((uint32 *) buffer.fData) + (sampleCount - 1); + + if (stream.BigEndian () || ifd.fPredictor == cpFloatingPoint || + ifd.fPredictor == cpFloatingPointX2 || + ifd.fPredictor == cpFloatingPointX4) + { + + for (uint32 index = 0; index < sampleCount; index++) + { + + *(dstPtr--) = DNG_FP24ToFloat (srcPtr); + + srcPtr -= 3; + + } + + } + + else + { + + for (uint32 index = 0; index < sampleCount; index++) + { + + uint8 input [3]; + + input [2] = srcPtr [0]; + input [1] = srcPtr [1]; + input [0] = srcPtr [2]; + + *(dstPtr--) = DNG_FP24ToFloat (input); + + srcPtr -= 3; + + } + + } + + buffer.fPixelSize = 4; + + } + + // Save the data. + + image.Put (buffer); + + return; + + } + + case ccUncompressed: + { + + if (ReadUncompressed (host, + ifd, + stream, + image, + tileArea, + plane, + planes, + uncompressedBuffer, + subTileBlockBuffer)) + { + + return; + + } + + break; + + } + + case ccJPEG: + { + + if (ifd.IsBaselineJPEG ()) + { + + // Baseline JPEG. + + if (ReadBaselineJPEG (host, + ifd, + stream, + image, + tileArea, + plane, + planes, + tileByteCount, + compressedBuffer.Get () ? compressedBuffer->Buffer_uint8 () : NULL)) + { + + return; + + } + + } + + else + { + + // Otherwise is should be lossless JPEG. + + if (ReadLosslessJPEG (host, + ifd, + stream, + image, + tileArea, + plane, + planes, + tileByteCount, + uncompressedBuffer, + subTileBlockBuffer)) + { + + return; + + } + + } + + break; + + } + + case ccLossyJPEG: + { + + if (ReadBaselineJPEG (host, + ifd, + stream, + image, + tileArea, + plane, + planes, + tileByteCount, + compressedBuffer.Get () ? compressedBuffer->Buffer_uint8 () : NULL)) + { + + return; + + } + + break; + + } + + default: + break; + + } + + ThrowBadFormat (); + + } + +/*****************************************************************************/ + +bool dng_read_image::CanRead (const dng_ifd &ifd) + { + + if (ifd.fImageWidth < 1 || + ifd.fImageLength < 1) + { + return false; + } + + if (ifd.fSamplesPerPixel < 1) + { + return false; + } + + if (ifd.fBitsPerSample [0] < 1) + { + return false; + } + + for (uint32 j = 1; j < Min_uint32 (ifd.fSamplesPerPixel, + kMaxSamplesPerPixel); j++) + { + + if (ifd.fBitsPerSample [j] != + ifd.fBitsPerSample [0]) + { + return false; + } + + if (ifd.fSampleFormat [j] != + ifd.fSampleFormat [0]) + { + return false; + } + + } + + if ((ifd.fPlanarConfiguration != pcInterleaved ) && + (ifd.fPlanarConfiguration != pcPlanar ) && + (ifd.fPlanarConfiguration != pcRowInterleaved)) + { + return false; + } + + if (ifd.fUsesStrips == ifd.fUsesTiles) + { + return false; + } + + uint32 tileCount = ifd.TilesPerImage (); + + if (tileCount < 1) + { + return false; + } + + bool needTileByteCounts = (ifd.TileByteCount (ifd.TileArea (0, 0)) == 0); + + if (tileCount == 1) + { + + if (needTileByteCounts) + { + + if (ifd.fTileByteCount [0] < 1) + { + return false; + } + + } + + } + + else + { + + if (ifd.fTileOffsetsCount != tileCount) + { + return false; + } + + if (needTileByteCounts) + { + + if (ifd.fTileByteCountsCount != tileCount) + { + return false; + } + + } + + } + + if (!CanReadTile (ifd)) + { + return false; + } + + return true; + + } + +/*****************************************************************************/ + +class dng_read_tiles_task : public dng_area_task + { + + private: + + dng_read_image &fReadImage; + + dng_host &fHost; + + const dng_ifd &fIFD; + + dng_stream &fStream; + + dng_image &fImage; + + dng_jpeg_image *fJPEGImage; + + dng_fingerprint *fJPEGTileDigest; + + uint32 fOuterSamples; + + uint32 fInnerSamples; + + uint32 fTilesDown; + + uint32 fTilesAcross; + + uint64 *fTileOffset; + + uint32 *fTileByteCount; + + uint32 fCompressedSize; + + uint32 fUncompressedSize; + + dng_mutex fMutex; + + uint32 fNextTileIndex; + + public: + + dng_read_tiles_task (dng_read_image &readImage, + dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage, + dng_fingerprint *jpegTileDigest, + uint32 outerSamples, + uint32 innerSamples, + uint32 tilesDown, + uint32 tilesAcross, + uint64 *tileOffset, + uint32 *tileByteCount, + uint32 compressedSize, + uint32 uncompressedSize) + + : fReadImage (readImage) + , fHost (host) + , fIFD (ifd) + , fStream (stream) + , fImage (image) + , fJPEGImage (jpegImage) + , fJPEGTileDigest (jpegTileDigest) + , fOuterSamples (outerSamples) + , fInnerSamples (innerSamples) + , fTilesDown (tilesDown) + , fTilesAcross (tilesAcross) + , fTileOffset (tileOffset) + , fTileByteCount (tileByteCount) + , fCompressedSize (compressedSize) + , fUncompressedSize (uncompressedSize) + , fMutex ("dng_read_tiles_task") + , fNextTileIndex (0) + + { + + fMinTaskArea = 16 * 16; + fUnitCell = dng_point (16, 16); + fMaxTileSize = dng_point (16, 16); + + } + + void Process (uint32 /* threadIndex */, + const dng_rect & /* tile */, + dng_abort_sniffer *sniffer) + { + + AutoPtr compressedBuffer; + AutoPtr uncompressedBuffer; + AutoPtr subTileBlockBuffer; + + if (!fJPEGImage) + { + compressedBuffer.Reset (fHost.Allocate (fCompressedSize)); + } + + if (fUncompressedSize) + { + uncompressedBuffer.Reset (fHost.Allocate (fUncompressedSize)); + } + + while (true) + { + + uint32 tileIndex; + uint32 byteCount; + + { + + dng_lock_mutex lock (&fMutex); + + if (fNextTileIndex == fOuterSamples * fTilesDown * fTilesAcross) + { + return; + } + + tileIndex = fNextTileIndex++; + + TempStreamSniffer noSniffer (fStream, NULL); + + fStream.SetReadPosition (fTileOffset [tileIndex]); + + byteCount = fTileByteCount [tileIndex]; + + if (fJPEGImage) + { + + fJPEGImage->fJPEGData [tileIndex] . Reset (fHost.Allocate (byteCount)); + + } + + fStream.Get (fJPEGImage ? fJPEGImage->fJPEGData [tileIndex]->Buffer () + : compressedBuffer->Buffer (), + byteCount); + + } + + dng_abort_sniffer::SniffForAbort (sniffer); + + if (fJPEGTileDigest) + { + + dng_md5_printer printer; + + printer.Process (compressedBuffer->Buffer (), + byteCount); + + fJPEGTileDigest [tileIndex] = printer.Result (); + + } + + dng_stream tileStream (fJPEGImage ? fJPEGImage->fJPEGData [tileIndex]->Buffer () + : compressedBuffer->Buffer (), + byteCount); + + tileStream.SetLittleEndian (fStream.LittleEndian ()); + + uint32 plane = tileIndex / (fTilesDown * fTilesAcross); + + uint32 rowIndex = (tileIndex - plane * fTilesDown * fTilesAcross) / fTilesAcross; + + uint32 colIndex = tileIndex - (plane * fTilesDown + rowIndex) * fTilesAcross; + + dng_rect tileArea = fIFD.TileArea (rowIndex, colIndex); + + dng_host host (&fHost.Allocator (), + sniffer); // Cannot use sniffer attached to main host + + fReadImage.ReadTile (host, + fIFD, + tileStream, + fImage, + tileArea, + plane, + fInnerSamples, + byteCount, + fJPEGImage ? fJPEGImage->fJPEGData [tileIndex] + : compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer); + + } + + } + + private: + + // Hidden copy constructor and assignment operator. + + dng_read_tiles_task (const dng_read_tiles_task &); + + dng_read_tiles_task & operator= (const dng_read_tiles_task &); + + }; + +/*****************************************************************************/ + +void dng_read_image::Read (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage, + dng_fingerprint *jpegDigest) + { + + uint32 tileIndex; + + // Deal with row interleaved images. + + if (ifd.fRowInterleaveFactor > 1 && + ifd.fRowInterleaveFactor < ifd.fImageLength) + { + + dng_ifd tempIFD (ifd); + + tempIFD.fRowInterleaveFactor = 1; + + dng_row_interleaved_image tempImage (image, + ifd.fRowInterleaveFactor); + + Read (host, + tempIFD, + stream, + tempImage, + jpegImage, + jpegDigest); + + return; + + } + + // Figure out inner and outer samples. + + uint32 innerSamples = 1; + uint32 outerSamples = 1; + + if (ifd.fPlanarConfiguration == pcPlanar) + { + outerSamples = ifd.fSamplesPerPixel; + } + else + { + innerSamples = ifd.fSamplesPerPixel; + } + + // Calculate number of tiles to read. + + uint32 tilesAcross = ifd.TilesAcross (); + uint32 tilesDown = ifd.TilesDown (); + + uint32 tileCount = tilesAcross * tilesDown * outerSamples; + + // Find the tile offsets. + + dng_memory_data tileOffsetData (tileCount * (uint32) sizeof (uint64)); + + uint64 *tileOffset = tileOffsetData.Buffer_uint64 (); + + if (tileCount <= dng_ifd::kMaxTileInfo) + { + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + tileOffset [tileIndex] = ifd.fTileOffset [tileIndex]; + + } + + } + + else + { + + stream.SetReadPosition (ifd.fTileOffsetsOffset); + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + tileOffset [tileIndex] = stream.TagValue_uint32 (ifd.fTileOffsetsType); + + } + + } + + // Quick validity check on tile offsets. + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + #if qDNGValidate + + if (tileOffset [tileIndex] < 8) + { + + ReportWarning ("Tile/Strip offset less than 8"); + + } + + #endif + + if (tileOffset [tileIndex] >= stream.Length ()) + { + + ThrowBadFormat (); + + } + + } + + // Buffer to hold the tile byte counts, if needed. + + dng_memory_data tileByteCountData; + + uint32 *tileByteCount = NULL; + + // If we can compute the number of bytes needed to store the + // data, we can split the read for each tile into sub-tiles. + + uint32 uncompressedSize = 0; + + uint32 subTileLength = ifd.fTileLength; + + if (ifd.TileByteCount (ifd.TileArea (0, 0)) != 0) + { + + uint32 bytesPerPixel = TagTypeSize (ifd.PixelType ()); + + uint32 bytesPerRow = ifd.fTileWidth * innerSamples * bytesPerPixel; + + subTileLength = Pin_uint32 (ifd.fSubTileBlockRows, + kImageBufferSize / bytesPerRow, + ifd.fTileLength); + + subTileLength = subTileLength / ifd.fSubTileBlockRows + * ifd.fSubTileBlockRows; + + uncompressedSize = subTileLength * bytesPerRow; + + } + + // Else we need to know the byte counts. + + else + { + + tileByteCountData.Allocate (tileCount * (uint32) sizeof (uint32)); + + tileByteCount = tileByteCountData.Buffer_uint32 (); + + if (tileCount <= dng_ifd::kMaxTileInfo) + { + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + tileByteCount [tileIndex] = ifd.fTileByteCount [tileIndex]; + + } + + } + + else + { + + stream.SetReadPosition (ifd.fTileByteCountsOffset); + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + tileByteCount [tileIndex] = stream.TagValue_uint32 (ifd.fTileByteCountsType); + + } + + } + + // Quick validity check on tile byte counts. + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + if (tileByteCount [tileIndex] < 1 || + tileByteCount [tileIndex] > stream.Length ()) + { + + ThrowBadFormat (); + + } + + } + + } + + // Find maximum tile size, if possible. + + uint32 maxTileByteCount = 0; + + if (tileByteCount) + { + + for (tileIndex = 0; tileIndex < tileCount; tileIndex++) + { + + maxTileByteCount = Max_uint32 (maxTileByteCount, + tileByteCount [tileIndex]); + + } + + } + + // Do we need a compressed data buffer? + + uint32 compressedSize = 0; + + bool needsCompressedBuffer = NeedsCompressedBuffer (ifd); + + if (needsCompressedBuffer) + { + + if (!tileByteCount) + { + ThrowBadFormat (); + } + + compressedSize = maxTileByteCount; + + } + + // Are we keeping the compressed JPEG image data? + + if (jpegImage) + { + + if (ifd.IsBaselineJPEG ()) + { + + jpegImage->fImageSize.h = ifd.fImageWidth; + jpegImage->fImageSize.v = ifd.fImageLength; + + jpegImage->fTileSize.h = ifd.fTileWidth; + jpegImage->fTileSize.v = ifd.fTileLength; + + jpegImage->fUsesStrips = ifd.fUsesStrips; + + jpegImage->fJPEGData.Reset (new dng_jpeg_image_tile_ptr [tileCount]); + + } + + else + { + + jpegImage = NULL; + + } + + } + + // Do we need to read the JPEG tables? + + if (ifd.fJPEGTablesOffset && ifd.fJPEGTablesCount) + { + + if (ifd.IsBaselineJPEG ()) + { + + fJPEGTables.Reset (host.Allocate (ifd.fJPEGTablesCount)); + + stream.SetReadPosition (ifd.fJPEGTablesOffset); + + stream.Get (fJPEGTables->Buffer (), + fJPEGTables->LogicalSize ()); + + } + + } + + AutoArray jpegTileDigest; + + if (jpegDigest) + { + + jpegTileDigest.Reset (new dng_fingerprint [tileCount + (fJPEGTables.Get () ? 1 : 0)]); + + } + + // Don't read planes we are not actually saving. + + outerSamples = Min_uint32 (image.Planes (), outerSamples); + + // See if we can do this read using multiple threads. + + bool useMultipleThreads = (outerSamples * tilesDown * tilesAcross >= 2) && + (host.PerformAreaTaskThreads () > 1) && + (maxTileByteCount > 0 && maxTileByteCount <= 1024 * 1024) && + (subTileLength == ifd.fTileLength) && + (ifd.fCompression != ccUncompressed); + +#if qImagecore + useMultipleThreads = false; +#endif + + if (useMultipleThreads) + { + + uint32 threadCount = Min_uint32 (outerSamples * tilesDown * tilesAcross, + host.PerformAreaTaskThreads ()); + + dng_read_tiles_task task (*this, + host, + ifd, + stream, + image, + jpegImage, + jpegTileDigest.Get (), + outerSamples, + innerSamples, + tilesDown, + tilesAcross, + tileOffset, + tileByteCount, + maxTileByteCount, + uncompressedSize); + + host.PerformAreaTask (task, + dng_rect (0, 0, 16, 16 * threadCount)); + + } + + // Else use a single thread to read all the tiles. + + else + { + + AutoPtr compressedBuffer; + AutoPtr uncompressedBuffer; + AutoPtr subTileBlockBuffer; + + if (uncompressedSize) + { + uncompressedBuffer.Reset (host.Allocate (uncompressedSize)); + } + + if (compressedSize && !jpegImage) + { + compressedBuffer.Reset (host.Allocate (compressedSize)); + } + + else if (jpegDigest) + { + compressedBuffer.Reset (host.Allocate (maxTileByteCount)); + } + + tileIndex = 0; + + for (uint32 plane = 0; plane < outerSamples; plane++) + { + + for (uint32 rowIndex = 0; rowIndex < tilesDown; rowIndex++) + { + + for (uint32 colIndex = 0; colIndex < tilesAcross; colIndex++) + { + + stream.SetReadPosition (tileOffset [tileIndex]); + + dng_rect tileArea = ifd.TileArea (rowIndex, colIndex); + + uint32 subTileCount = (tileArea.H () + subTileLength - 1) / + subTileLength; + + for (uint32 subIndex = 0; subIndex < subTileCount; subIndex++) + { + + host.SniffForAbort (); + + dng_rect subArea (tileArea); + + subArea.t = tileArea.t + subIndex * subTileLength; + + subArea.b = Min_int32 (subArea.t + subTileLength, + tileArea.b); + + uint32 subByteCount; + + if (tileByteCount) + { + subByteCount = tileByteCount [tileIndex]; + } + else + { + subByteCount = ifd.TileByteCount (subArea); + } + + if (jpegImage) + { + + jpegImage->fJPEGData [tileIndex].Reset (host.Allocate (subByteCount)); + + stream.Get (jpegImage->fJPEGData [tileIndex]->Buffer (), subByteCount); + + stream.SetReadPosition (tileOffset [tileIndex]); + + } + + else if ((needsCompressedBuffer || jpegDigest) && subByteCount) + { + + stream.Get (compressedBuffer->Buffer (), subByteCount); + + if (jpegDigest) + { + + dng_md5_printer printer; + + printer.Process (compressedBuffer->Buffer (), + subByteCount); + + jpegTileDigest [tileIndex] = printer.Result (); + + } + + } + + ReadTile (host, + ifd, + stream, + image, + subArea, + plane, + innerSamples, + subByteCount, + jpegImage ? jpegImage->fJPEGData [tileIndex] : compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer); + + } + + tileIndex++; + + } + + } + + } + + } + + // Finish up JPEG digest computation, if needed. + + if (jpegDigest) + { + + if (fJPEGTables.Get ()) + { + + dng_md5_printer printer; + + printer.Process (fJPEGTables->Buffer (), + fJPEGTables->LogicalSize ()); + + jpegTileDigest [tileCount] = printer.Result (); + + } + + dng_md5_printer printer2; + + for (uint32 j = 0; j < tileCount + (fJPEGTables.Get () ? 1 : 0); j++) + { + + printer2.Process (jpegTileDigest [j].data, + dng_fingerprint::kDNGFingerprintSize); + + } + + *jpegDigest = printer2.Result (); + + } + + // Keep the JPEG table in the jpeg image, if any. + + if (jpegImage) + { + + jpegImage->fJPEGTables.Reset (fJPEGTables.Release ()); + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_read_image.h b/source/lib/dng_sdk/dng_read_image.h new file mode 100644 index 0000000..bd79e55 --- /dev/null +++ b/source/lib/dng_sdk/dng_read_image.h @@ -0,0 +1,182 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_read_image.h#2 $ */ +/* $DateTime: 2012/06/05 11:05:39 $ */ +/* $Change: 833352 $ */ +/* $Author: tknoll $ */ + +/** \file + * Support for DNG image reading. + */ + +/*****************************************************************************/ + +#ifndef __dng_read_image__ +#define __dng_read_image__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_image.h" +#include "dng_memory.h" +#include "dng_types.h" + +/******************************************************************************/ + +bool DecodePackBits (dng_stream &stream, + uint8 *dPtr, + int32 dstCount); + +/*****************************************************************************/ + +class dng_row_interleaved_image: public dng_image + { + + private: + + dng_image &fImage; + + uint32 fFactor; + + public: + + dng_row_interleaved_image (dng_image &image, + uint32 factor); + + virtual void DoGet (dng_pixel_buffer &buffer) const; + + virtual void DoPut (const dng_pixel_buffer &buffer); + + private: + + int32 MapRow (int32 row) const; + + }; + +/*****************************************************************************/ + +/// \brief +/// +/// + +class dng_read_image + { + + friend class dng_read_tiles_task; + + protected: + + enum + { + + // Target size for buffer used to copy data to the image. + + kImageBufferSize = 128 * 1024 + + }; + + AutoPtr fJPEGTables; + + public: + + dng_read_image (); + + virtual ~dng_read_image (); + + /// + /// \param + + virtual bool CanRead (const dng_ifd &ifd); + + /// + /// \param host Host used for memory allocation, progress updating, and abort testing. + /// \param ifd + /// \param stream Stream to read image data from. + /// \param image Result image to populate. + + virtual void Read (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + dng_jpeg_image *jpegImage, + dng_fingerprint *jpegDigest); + + protected: + + virtual bool ReadUncompressed (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer); + + virtual void DecodeLossyJPEG (dng_host &host, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 photometricInterpretation, + uint32 jpegDataSize, + uint8 *jpegDataInMemory); + + virtual bool ReadBaselineJPEG (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + uint8 *jpegDataInMemory); + + virtual bool ReadLosslessJPEG (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer); + + virtual bool CanReadTile (const dng_ifd &ifd); + + virtual bool NeedsCompressedBuffer (const dng_ifd &ifd); + + virtual void ByteSwapBuffer (dng_host &host, + dng_pixel_buffer &buffer); + + virtual void DecodePredictor (dng_host &host, + const dng_ifd &ifd, + dng_pixel_buffer &buffer); + + virtual void ReadTile (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_rect.cpp b/source/lib/dng_sdk/dng_rect.cpp new file mode 100644 index 0000000..c45e03f --- /dev/null +++ b/source/lib/dng_sdk/dng_rect.cpp @@ -0,0 +1,168 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_rect.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_rect.h" + +#include "dng_utils.h" + +/*****************************************************************************/ + +bool dng_rect::operator== (const dng_rect &rect) const + { + + return (rect.t == t) && + (rect.l == l) && + (rect.b == b) && + (rect.r == r); + + } + +/*****************************************************************************/ + +bool dng_rect::IsZero () const + { + + return (t == 0) && (l == 0) && (b == 0) && (r == 0); + + } + +/*****************************************************************************/ + +bool dng_rect_real64::operator== (const dng_rect_real64 &rect) const + { + + return (rect.t == t) && + (rect.l == l) && + (rect.b == b) && + (rect.r == r); + + } + +/*****************************************************************************/ + +bool dng_rect_real64::IsZero () const + { + + return (t == 0.0) && (l == 0.0) && (b == 0.0) && (r == 0.0); + + } + +/*****************************************************************************/ + +dng_rect operator& (const dng_rect &a, + const dng_rect &b) + { + + dng_rect c; + + c.t = Max_int32 (a.t, b.t); + c.l = Max_int32 (a.l, b.l); + + c.b = Min_int32 (a.b, b.b); + c.r = Min_int32 (a.r, b.r); + + if (c.IsEmpty ()) + { + + c = dng_rect (); + + } + + return c; + + } + +/*****************************************************************************/ + +dng_rect operator| (const dng_rect &a, + const dng_rect &b) + { + + if (a.IsEmpty ()) + { + return b; + } + + if (b.IsEmpty ()) + { + return a; + } + + dng_rect c; + + c.t = Min_int32 (a.t, b.t); + c.l = Min_int32 (a.l, b.l); + + c.b = Max_int32 (a.b, b.b); + c.r = Max_int32 (a.r, b.r); + + return c; + + } + +/*****************************************************************************/ + +dng_rect_real64 operator& (const dng_rect_real64 &a, + const dng_rect_real64 &b) + { + + dng_rect_real64 c; + + c.t = Max_real64 (a.t, b.t); + c.l = Max_real64 (a.l, b.l); + + c.b = Min_real64 (a.b, b.b); + c.r = Min_real64 (a.r, b.r); + + if (c.IsEmpty ()) + { + + c = dng_rect_real64 (); + + } + + return c; + + } + +/*****************************************************************************/ + +dng_rect_real64 operator| (const dng_rect_real64 &a, + const dng_rect_real64 &b) + { + + if (a.IsEmpty ()) + { + return b; + } + + if (b.IsEmpty ()) + { + return a; + } + + dng_rect_real64 c; + + c.t = Min_real64 (a.t, b.t); + c.l = Min_real64 (a.l, b.l); + + c.b = Max_real64 (a.b, b.b); + c.r = Max_real64 (a.r, b.r); + + return c; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_rect.h b/source/lib/dng_sdk/dng_rect.h new file mode 100644 index 0000000..16c4b29 --- /dev/null +++ b/source/lib/dng_sdk/dng_rect.h @@ -0,0 +1,494 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_rect.h#2 $ */ +/* $DateTime: 2012/06/01 07:28:57 $ */ +/* $Change: 832715 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_rect__ +#define __dng_rect__ + +/*****************************************************************************/ + +#include "dng_types.h" +#include "dng_point.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +class dng_rect + { + + public: + + int32 t; + int32 l; + int32 b; + int32 r; + + public: + + dng_rect () + : t (0) + , l (0) + , b (0) + , r (0) + { + } + + dng_rect (int32 tt, int32 ll, int32 bb, int32 rr) + : t (tt) + , l (ll) + , b (bb) + , r (rr) + { + } + + dng_rect (uint32 h, uint32 w) + : t (0) + , l (0) + , b ((int32) h) + , r ((int32) w) + { + } + + dng_rect (const dng_point &size) + : t (0) + , l (0) + , b (size.v) + , r (size.h) + { + } + + void Clear () + { + *this = dng_rect (); + } + + bool operator== (const dng_rect &rect) const; + + bool operator!= (const dng_rect &rect) const + { + return !(*this == rect); + } + + bool IsZero () const; + + bool NotZero () const + { + return !IsZero (); + } + + bool IsEmpty () const + { + return (t >= b) || (l >= r); + } + + bool NotEmpty () const + { + return !IsEmpty (); + } + + uint32 W () const + { + return (r >= l ? (uint32) (r - l) : 0); + } + + uint32 H () const + { + return (b >= t ? (uint32) (b - t) : 0); + } + + dng_point TL () const + { + return dng_point (t, l); + } + + dng_point TR () const + { + return dng_point (t, r); + } + + dng_point BL () const + { + return dng_point (b, l); + } + + dng_point BR () const + { + return dng_point (b, r); + } + + dng_point Size () const + { + return dng_point ((int32) H (), (int32) W ()); + } + + real64 Diagonal () const + { + return hypot ((real64) W (), + (real64) H ()); + } + + }; + +/*****************************************************************************/ + +class dng_rect_real64 + { + + public: + + real64 t; + real64 l; + real64 b; + real64 r; + + public: + + dng_rect_real64 () + : t (0.0) + , l (0.0) + , b (0.0) + , r (0.0) + { + } + + dng_rect_real64 (real64 tt, real64 ll, real64 bb, real64 rr) + : t (tt) + , l (ll) + , b (bb) + , r (rr) + { + } + + dng_rect_real64 (real64 h, real64 w) + : t (0) + , l (0) + , b (h) + , r (w) + { + } + + dng_rect_real64 (const dng_point_real64 &size) + : t (0) + , l (0) + , b (size.v) + , r (size.h) + { + } + + dng_rect_real64 (const dng_point_real64 &pt1, + const dng_point_real64 &pt2) + : t (Min_real64 (pt1.v, pt2.v)) + , l (Min_real64 (pt1.h, pt2.h)) + , b (Max_real64 (pt1.v, pt2.v)) + , r (Max_real64 (pt1.h, pt2.h)) + { + } + + dng_rect_real64 (const dng_rect &rect) + : t ((real64) rect.t) + , l ((real64) rect.l) + , b ((real64) rect.b) + , r ((real64) rect.r) + { + } + + void Clear () + { + *this = dng_point_real64 (); + } + + bool operator== (const dng_rect_real64 &rect) const; + + bool operator!= (const dng_rect_real64 &rect) const + { + return !(*this == rect); + } + + bool IsZero () const; + + bool NotZero () const + { + return !IsZero (); + } + + bool IsEmpty () const + { + return (t >= b) || (l >= r); + } + + bool NotEmpty () const + { + return !IsEmpty (); + } + + real64 W () const + { + return Max_real64 (r - l, 0.0); + } + + real64 H () const + { + return Max_real64 (b - t, 0.0); + } + + dng_point_real64 TL () const + { + return dng_point_real64 (t, l); + } + + dng_point_real64 TR () const + { + return dng_point_real64 (t, r); + } + + dng_point_real64 BL () const + { + return dng_point_real64 (b, l); + } + + dng_point_real64 BR () const + { + return dng_point_real64 (b, r); + } + + dng_point_real64 Size () const + { + return dng_point_real64 (H (), W ()); + } + + dng_rect Round () const + { + return dng_rect (Round_int32 (t), + Round_int32 (l), + Round_int32 (b), + Round_int32 (r)); + } + + real64 Diagonal () const + { + return hypot (W (), H ()); + } + + }; + +/*****************************************************************************/ + +dng_rect operator& (const dng_rect &a, + const dng_rect &b); + +dng_rect operator| (const dng_rect &a, + const dng_rect &b); + +/*****************************************************************************/ + +dng_rect_real64 operator& (const dng_rect_real64 &a, + const dng_rect_real64 &b); + +dng_rect_real64 operator| (const dng_rect_real64 &a, + const dng_rect_real64 &b); + +/*****************************************************************************/ + +inline dng_rect operator+ (const dng_rect &a, + const dng_point &b) + { + + return dng_rect (a.t + b.v, + a.l + b.h, + a.b + b.v, + a.r + b.h); + + } + +/*****************************************************************************/ + +inline dng_rect_real64 operator+ (const dng_rect_real64 &a, + const dng_point_real64 &b) + { + + return dng_rect_real64 (a.t + b.v, + a.l + b.h, + a.b + b.v, + a.r + b.h); + + } + +/*****************************************************************************/ + +inline dng_rect operator- (const dng_rect &a, + const dng_point &b) + { + + return dng_rect (a.t - b.v, + a.l - b.h, + a.b - b.v, + a.r - b.h); + + } + +/*****************************************************************************/ + +inline dng_rect_real64 operator- (const dng_rect_real64 &a, + const dng_point_real64 &b) + { + + return dng_rect_real64 (a.t - b.v, + a.l - b.h, + a.b - b.v, + a.r - b.h); + + } + +/*****************************************************************************/ + +inline dng_rect Transpose (const dng_rect &a) + { + + return dng_rect (a.l, a.t, a.r, a.b); + + } + +/*****************************************************************************/ + +inline dng_rect_real64 Transpose (const dng_rect_real64 &a) + { + + return dng_rect_real64 (a.l, a.t, a.r, a.b); + + } + +/*****************************************************************************/ + +inline void HalfRect (dng_rect &rect) + { + + rect.r = rect.l + (int32) (rect.W () >> 1); + rect.b = rect.t + (int32) (rect.H () >> 1); + + } + +/*****************************************************************************/ + +inline void DoubleRect (dng_rect &rect) + { + + rect.r = rect.l + (int32) (rect.W () << 1); + rect.b = rect.t + (int32) (rect.H () << 1); + + } + +/*****************************************************************************/ + +inline void InnerPadRect (dng_rect &rect, + int32 pad) + { + + rect.l += pad; + rect.r -= pad; + rect.t += pad; + rect.b -= pad; + + } + +/*****************************************************************************/ + +inline void OuterPadRect (dng_rect &rect, + int32 pad) + { + + InnerPadRect (rect, -pad); + + } + +/*****************************************************************************/ + +inline void InnerPadRectH (dng_rect &rect, + int32 pad) + { + + rect.l += pad; + rect.r -= pad; + + } + +/*****************************************************************************/ + +inline void InnerPadRectV (dng_rect &rect, + int32 pad) + { + + rect.t += pad; + rect.b -= pad; + + } + +/*****************************************************************************/ + +inline dng_rect MakeHalfRect (const dng_rect &rect) + { + + dng_rect out = rect; + + HalfRect (out); + + return out; + + } + +/*****************************************************************************/ + +inline dng_rect MakeDoubleRect (const dng_rect &rect) + { + + dng_rect out = rect; + + DoubleRect (out); + + return out; + + } + +/*****************************************************************************/ + +inline dng_rect MakeInnerPadRect (const dng_rect &rect, + int32 pad) + { + + dng_rect out = rect; + + InnerPadRect (out, pad); + + return out; + + } + +/*****************************************************************************/ + +inline dng_rect MakeOuterPadRect (const dng_rect &rect, + int32 pad) + { + + dng_rect out = rect; + + OuterPadRect (out, pad); + + return out; + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_ref_counted_block.cpp b/source/lib/dng_sdk/dng_ref_counted_block.cpp new file mode 100644 index 0000000..32df295 --- /dev/null +++ b/source/lib/dng_sdk/dng_ref_counted_block.cpp @@ -0,0 +1,190 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_ref_counted_block.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include + +#include "dng_ref_counted_block.h" + +#include "dng_exceptions.h" + +#include "gpr_allocator.h" + +#include "stdc_includes.h" + +/*****************************************************************************/ + +dng_ref_counted_block::dng_ref_counted_block () + + : fBuffer (NULL) + + { + + } + +/*****************************************************************************/ + +dng_ref_counted_block::dng_ref_counted_block (uint32 size) + + : fBuffer (NULL) + + { + + Allocate (size); + + } + +/*****************************************************************************/ + +dng_ref_counted_block::~dng_ref_counted_block () + { + + Clear (); + + } + +/*****************************************************************************/ + +void dng_ref_counted_block::Allocate (uint32 size) + { + + Clear (); + + if (size) + { + fBuffer = gpr_global_malloc(size + sizeof (header)); + + if (!fBuffer) + { + + ThrowMemoryFull (); + + } + + new (fBuffer) header (size); + + } + + } + +/*****************************************************************************/ + +void dng_ref_counted_block::Clear () + { + + if (fBuffer) + { + + + bool doFree = false; + + header *blockHeader = (struct header *)fBuffer; + + { + + dng_lock_mutex lock (&blockHeader->fMutex); + + if (--blockHeader->fRefCount == 0) + doFree = true; + } + + if (doFree) + { + + blockHeader->~header (); + + gpr_global_free( fBuffer ); + + } + + fBuffer = NULL; + + } + + } + +/*****************************************************************************/ + +dng_ref_counted_block::dng_ref_counted_block (const dng_ref_counted_block &data) + : fBuffer (NULL) + { + + header *blockHeader = (struct header *)data.fBuffer; + + dng_lock_mutex lock (&blockHeader->fMutex); + + blockHeader->fRefCount++; + + fBuffer = blockHeader; + + } + +/*****************************************************************************/ + +dng_ref_counted_block & dng_ref_counted_block::operator= (const dng_ref_counted_block &data) + { + + if (this != &data) + { + Clear (); + + header *blockHeader = (struct header *)data.fBuffer; + + dng_lock_mutex lock (&blockHeader->fMutex); + + blockHeader->fRefCount++; + + fBuffer = blockHeader; + + } + + return *this; + + } + +/*****************************************************************************/ + +void dng_ref_counted_block::EnsureWriteable () + { + + if (fBuffer) + { + + header *possiblySharedHeader = (header *)fBuffer; + + { + + dng_lock_mutex lock (&possiblySharedHeader->fMutex); + + if (possiblySharedHeader->fRefCount > 1) + { + + fBuffer = NULL; + + Allocate ((uint32)possiblySharedHeader->fSize); + + memcpy (Buffer (), + ((char *)possiblySharedHeader) + sizeof (struct header), // could just do + 1 w/o cast, but this makes the type mixing more explicit + possiblySharedHeader->fSize); + + possiblySharedHeader->fRefCount--; + + } + + } + + } + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_ref_counted_block.h b/source/lib/dng_sdk/dng_ref_counted_block.h new file mode 100644 index 0000000..cdda745 --- /dev/null +++ b/source/lib/dng_sdk/dng_ref_counted_block.h @@ -0,0 +1,291 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_ref_counted_block.h#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** Support for a refcounted block, with optional copy-on-write + */ + +/*****************************************************************************/ + +#ifndef __dng_ref_counted_block__ +#define __dng_ref_counted_block__ + +/*****************************************************************************/ + +#include "dng_types.h" +#include "dng_mutex.h" + +/*****************************************************************************/ + +/// \brief Class to provide resource acquisition is instantiation discipline +/// for small memory allocations. +/// +/// This class does not use dng_memory_allocator for memory allocation. + +class dng_ref_counted_block + { + + private: + + struct header + { + + dng_mutex fMutex; + + uint32 fRefCount; + + uint32 fSize; + + header (uint32 size) + : fMutex ("dng_ref_counted_block") + , fRefCount (1) + , fSize (size) + { + } + + ~header () + { + } + + }; + + void *fBuffer; + + public: + + + /// Construct an empty memory buffer using malloc. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + dng_ref_counted_block (); + + /// Construct memory buffer of size bytes using malloc. + /// \param size Number of bytes of memory needed. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + dng_ref_counted_block (uint32 size); + + /// Release memory buffer using free. + + ~dng_ref_counted_block (); + + /// Copy constructore, which takes a reference to data and does not copy the block. + + dng_ref_counted_block (const dng_ref_counted_block &data); + + /// Assignment operatore takes a reference to right hand side and does not copy the data. + + dng_ref_counted_block & operator= (const dng_ref_counted_block &data); + + /// Clear existing memory buffer and allocate new memory of size bytes. + /// \param size Number of bytes of memory needed. + /// \exception dng_memory_full with fErrorCode equal to dng_error_memory. + + void Allocate (uint32 size); + + /// Release any allocated memory using free. Object is still valid and + /// Allocate can be called again. + + void Clear (); + + /// If there is only one reference, do nothing, otherwise copy the data into a new block and return an object with that block as the data. + + void EnsureWriteable (); + + /// Return pointer to allocated memory as a void *.. + /// \retval void * valid for as many bytes as were allocated. + + uint32 LogicalSize () + { + return ((header *)fBuffer)->fSize; + } + + void * Buffer () + { + return (void *)((char *)fBuffer + sizeof (header)); + } + + /// Return pointer to allocated memory as a const void *. + /// \retval const void * valid for as many bytes as were allocated. + + const void * Buffer () const + { + return (const void *)((char *)fBuffer + sizeof (header)); + } + + /// Return pointer to allocated memory as a char *. + /// \retval char * valid for as many bytes as were allocated. + + char * Buffer_char () + { + return (char *) Buffer (); + } + + /// Return pointer to allocated memory as a const char *. + /// \retval const char * valid for as many bytes as were allocated. + + const char * Buffer_char () const + { + return (const char *) Buffer (); + } + + /// Return pointer to allocated memory as a uint8 *. + /// \retval uint8 * valid for as many bytes as were allocated. + + uint8 * Buffer_uint8 () + { + return (uint8 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint8 *. + /// \retval const uint8 * valid for as many bytes as were allocated. + + const uint8 * Buffer_uint8 () const + { + return (const uint8 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint16 *. + /// \retval uint16 * valid for as many bytes as were allocated. + + uint16 * Buffer_uint16 () + { + return (uint16 *) Buffer (); + } + + /// Return pointer to allocated memory as a const uint16 *. + /// \retval const uint16 * valid for as many bytes as were allocated. + + const uint16 * Buffer_uint16 () const + { + return (const uint16 *) Buffer (); + } + + /// Return pointer to allocated memory as a int16 *. + /// \retval int16 * valid for as many bytes as were allocated. + + int16 * Buffer_int16 () + { + return (int16 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int16 *. + /// \retval const int16 * valid for as many bytes as were allocated. + + const int16 * Buffer_int16 () const + { + return (const int16 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint32 *. + /// \retval uint32 * valid for as many bytes as were allocated. + + uint32 * Buffer_uint32 () + { + return (uint32 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint32 *. + /// \retval uint32 * valid for as many bytes as were allocated. + + const uint32 * Buffer_uint32 () const + { + return (const uint32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int32 *. + /// \retval const int32 * valid for as many bytes as were allocated. + + int32 * Buffer_int32 () + { + return (int32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int32 *. + /// \retval const int32 * valid for as many bytes as were allocated. + + const int32 * Buffer_int32 () const + { + return (const int32 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint64 *. + /// \retval uint64 * valid for as many bytes as were allocated. + + uint64 * Buffer_uint64 () + { + return (uint64 *) Buffer (); + } + + /// Return pointer to allocated memory as a uint64 *. + /// \retval uint64 * valid for as many bytes as were allocated. + + const uint64 * Buffer_uint64 () const + { + return (const uint64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int64 *. + /// \retval const int64 * valid for as many bytes as were allocated. + + int64 * Buffer_int64 () + { + return (int64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const int64 *. + /// \retval const int64 * valid for as many bytes as were allocated. + + const int64 * Buffer_int64 () const + { + return (const int64 *) Buffer (); + } + + /// Return pointer to allocated memory as a real32 *. + /// \retval real32 * valid for as many bytes as were allocated. + + real32 * Buffer_real32 () + { + return (real32 *) Buffer (); + } + + /// Return pointer to allocated memory as a const real32 *. + /// \retval const real32 * valid for as many bytes as were allocated. + + const real32 * Buffer_real32 () const + { + return (const real32 *) Buffer (); + } + + /// Return pointer to allocated memory as a real64 *. + /// \retval real64 * valid for as many bytes as were allocated. + + real64 * Buffer_real64 () + { + return (real64 *) Buffer (); + } + + /// Return pointer to allocated memory as a const real64 *. + /// \retval const real64 * valid for as many bytes as were allocated. + + const real64 * Buffer_real64 () const + { + return (const real64 *) Buffer (); + } + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_reference.cpp b/source/lib/dng_sdk/dng_reference.cpp new file mode 100644 index 0000000..5d35378 --- /dev/null +++ b/source/lib/dng_sdk/dng_reference.cpp @@ -0,0 +1,2783 @@ +/*****************************************************************************/ +// Copyright 2006-2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_reference.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_reference.h" + +#include "dng_1d_table.h" +#include "dng_hue_sat_map.h" +#include "dng_matrix.h" +#include "dng_resample.h" +#include "dng_utils.h" + +#include "stdc_includes.h" + +/*****************************************************************************/ + +// This module contains routines that should be as fast as possible, even +// at the expense of slight code size increases. + +#include "dng_fast_module.h" + +/*****************************************************************************/ + +void RefZeroBytes (void *dPtr, + uint32 count) + { + + memset (dPtr, 0, count); + + } + +/*****************************************************************************/ + +void RefCopyBytes (const void *sPtr, + void *dPtr, + uint32 count) + { + + memcpy (dPtr, sPtr, count); + + } + +/*****************************************************************************/ + +void RefSwapBytes16 (uint16 *dPtr, + uint32 count) + { + + for (uint32 j = 0; j < count; j++) + { + + dPtr [j] = SwapBytes16 (dPtr [j]); + + } + + } + +/*****************************************************************************/ + +void RefSwapBytes32 (uint32 *dPtr, + uint32 count) + { + + for (uint32 j = 0; j < count; j++) + { + + dPtr [j] = SwapBytes32 (dPtr [j]); + + } + + } + +/*****************************************************************************/ + +void RefSetArea8 (uint8 *dPtr, + uint8 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + uint8 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + uint8 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = value; + + dPtr2 += planeStep; + + } + + dPtr1 += colStep; + + } + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefSetArea16 (uint16 *dPtr, + uint16 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + uint16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = value; + + dPtr2 += planeStep; + + } + + dPtr1 += colStep; + + } + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefSetArea32 (uint32 *dPtr, + uint32 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + uint32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + uint32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = value; + + dPtr2 += planeStep; + + } + + dPtr1 += colStep; + + } + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea8 (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr; + uint8 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + uint8 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint16 *sPtr1 = sPtr; + uint16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint16 *sPtr2 = sPtr1; + uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea32 (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint32 *sPtr1 = sPtr; + uint32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint32 *sPtr2 = sPtr1; + uint32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea8_16 (const uint8 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr; + uint16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea8_S16 (const uint8 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr; + int16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + int16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + int16 x = *sPtr; + + *dPtr2 = x ^ 0x8000; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea8_32 (const uint8 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr; + uint32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + uint32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea16_S16 (const uint16 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint16 *sPtr1 = sPtr; + int16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint16 *sPtr2 = sPtr1; + int16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2 ^ 0x8000; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea16_32 (const uint16 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint16 *sPtr1 = sPtr; + uint32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint16 *sPtr2 = sPtr1; + uint32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea8_R32 (const uint8 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + real32 scale = 1.0f / (real32) pixelRange; + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr; + real32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + real32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = scale * (real32) *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyArea16_R32 (const uint16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + real32 scale = 1.0f / (real32) pixelRange; + + for (uint32 row = 0; row < rows; row++) + { + + const uint16 *sPtr1 = sPtr; + real32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint16 *sPtr2 = sPtr1; + real32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = scale * (real32) *sPtr2; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyAreaS16_R32 (const int16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + real32 scale = 1.0f / (real32) pixelRange; + + for (uint32 row = 0; row < rows; row++) + { + + const int16 *sPtr1 = sPtr; + real32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const int16 *sPtr2 = sPtr1; + real32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + int32 x = (*sPtr ^ 0x8000); + + *dPtr2 = scale * (real32) x; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyAreaR32_8 (const real32 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + real32 scale = (real32) pixelRange; + + for (uint32 row = 0; row < rows; row++) + { + + const real32 *sPtr1 = sPtr; + uint8 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const real32 *sPtr2 = sPtr1; + uint8 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = (uint8) (Pin_Overrange (*sPtr2) * scale + 0.5f); + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyAreaR32_16 (const real32 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + real32 scale = (real32) pixelRange; + + for (uint32 row = 0; row < rows; row++) + { + + const real32 *sPtr1 = sPtr; + uint16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const real32 *sPtr2 = sPtr1; + uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = (uint16) (Pin_Overrange (*sPtr2) * scale + 0.5f); + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefCopyAreaR32_S16 (const real32 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange) + { + + real32 scale = (real32) pixelRange; + + for (uint32 row = 0; row < rows; row++) + { + + const real32 *sPtr1 = sPtr; + int16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const real32 *sPtr2 = sPtr1; + int16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + int32 x = (int32) (Pin_Overrange (*sPtr2) * scale + 0.5f); + + *dPtr2 = (int16) (x ^ 0x8000); + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + } + +/*****************************************************************************/ + +void RefRepeatArea8 (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH) + { + + const uint8 *sPtr0 = sPtr + phaseV * rowStep + + phaseH * colStep; + + int32 backStepV = (repeatV - 1) * rowStep; + int32 backStepH = (repeatH - 1) * colStep; + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr0; + uint8 *dPtr1 = dPtr; + + uint32 colPhase = phaseH; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + uint8 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += planeStep; + dPtr2 += planeStep; + + } + + if (++colPhase == repeatH) + { + colPhase = 0; + sPtr1 -= backStepH; + } + else + { + sPtr1 += colStep; + } + + dPtr1 += colStep; + + } + + if (++phaseV == repeatV) + { + phaseV = 0; + sPtr0 -= backStepV; + } + else + { + sPtr0 += rowStep; + } + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefRepeatArea16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH) + { + + const uint16 *sPtr0 = sPtr + phaseV * rowStep + + phaseH * colStep; + + int32 backStepV = (repeatV - 1) * rowStep; + int32 backStepH = (repeatH - 1) * colStep; + + for (uint32 row = 0; row < rows; row++) + { + + const uint16 *sPtr1 = sPtr0; + uint16 *dPtr1 = dPtr; + + uint32 colPhase = phaseH; + + for (uint32 col = 0; col < cols; col++) + { + + const uint16 *sPtr2 = sPtr1; + uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += planeStep; + dPtr2 += planeStep; + + } + + if (++colPhase == repeatH) + { + colPhase = 0; + sPtr1 -= backStepH; + } + else + { + sPtr1 += colStep; + } + + dPtr1 += colStep; + + } + + if (++phaseV == repeatV) + { + phaseV = 0; + sPtr0 -= backStepV; + } + else + { + sPtr0 += rowStep; + } + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefRepeatArea32 (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH) + { + + const uint32 *sPtr0 = sPtr + phaseV * rowStep + + phaseH * colStep; + + int32 backStepV = (repeatV - 1) * rowStep; + int32 backStepH = (repeatH - 1) * colStep; + + for (uint32 row = 0; row < rows; row++) + { + + const uint32 *sPtr1 = sPtr0; + uint32 *dPtr1 = dPtr; + + uint32 colPhase = phaseH; + + for (uint32 col = 0; col < cols; col++) + { + + const uint32 *sPtr2 = sPtr1; + uint32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 = *sPtr2; + + sPtr2 += planeStep; + dPtr2 += planeStep; + + } + + if (++colPhase == repeatH) + { + colPhase = 0; + sPtr1 -= backStepH; + } + else + { + sPtr1 += colStep; + } + + dPtr1 += colStep; + + } + + if (++phaseV == repeatV) + { + phaseV = 0; + sPtr0 -= backStepV; + } + else + { + sPtr0 += rowStep; + } + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefShiftRight16 (uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 shift) + { + + for (uint32 row = 0; row < rows; row++) + { + + uint16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + *dPtr2 >>= shift; + + dPtr2 += planeStep; + + } + + dPtr1 += colStep; + + } + + dPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefBilinearRow16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const uint16 * const * kernWeights, + uint32 sShift) + { + + for (uint32 j = 0; j < cols; j++) + { + + const uint16 *p = sPtr + (j >> sShift); + + uint32 count = kernCounts [patPhase]; + + const int32 *offsets = kernOffsets [patPhase]; + const uint16 *weights = kernWeights [patPhase]; + + if (++patPhase == patCount) + { + patPhase = 0; + } + + uint32 total = 128; + + for (uint32 k = 0; k < count; k++) + { + + int32 offset = offsets [k]; + uint32 weight = weights [k]; + + uint32 pixel = p [offset]; + + total += pixel * weight; + + } + + dPtr [j] = (uint16) (total >> 8); + + } + + } + +/*****************************************************************************/ + +void RefBilinearRow32 (const real32 *sPtr, + real32 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const real32 * const * kernWeights, + uint32 sShift) + { + + for (uint32 j = 0; j < cols; j++) + { + + const real32 *p = sPtr + (j >> sShift); + + uint32 count = kernCounts [patPhase]; + + const int32 *offsets = kernOffsets [patPhase]; + const real32 *weights = kernWeights [patPhase]; + + if (++patPhase == patCount) + { + patPhase = 0; + } + + real32 total = 0.0f; + + for (uint32 k = 0; k < count; k++) + { + + int32 offset = offsets [k]; + real32 weight = weights [k]; + + real32 pixel = p [offset]; + + total += pixel * weight; + + } + + dPtr [j] = total; + + } + + } + +/*****************************************************************************/ + +void RefBaselineABCtoRGB (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB) + { + + real32 clipA = (real32) cameraWhite [0]; + real32 clipB = (real32) cameraWhite [1]; + real32 clipC = (real32) cameraWhite [2]; + + real32 m00 = (real32) cameraToRGB [0] [0]; + real32 m01 = (real32) cameraToRGB [0] [1]; + real32 m02 = (real32) cameraToRGB [0] [2]; + + real32 m10 = (real32) cameraToRGB [1] [0]; + real32 m11 = (real32) cameraToRGB [1] [1]; + real32 m12 = (real32) cameraToRGB [1] [2]; + + real32 m20 = (real32) cameraToRGB [2] [0]; + real32 m21 = (real32) cameraToRGB [2] [1]; + real32 m22 = (real32) cameraToRGB [2] [2]; + + for (uint32 col = 0; col < count; col++) + { + + real32 A = sPtrA [col]; + real32 B = sPtrB [col]; + real32 C = sPtrC [col]; + + A = Min_real32 (A, clipA); + B = Min_real32 (B, clipB); + C = Min_real32 (C, clipC); + + real32 r = m00 * A + m01 * B + m02 * C; + real32 g = m10 * A + m11 * B + m12 * C; + real32 b = m20 * A + m21 * B + m22 * C; + + r = Pin_real32 (0.0f, r, 1.0f); + g = Pin_real32 (0.0f, g, 1.0f); + b = Pin_real32 (0.0f, b, 1.0f); + + dPtrR [col] = r; + dPtrG [col] = g; + dPtrB [col] = b; + + } + + } + +/*****************************************************************************/ + +void RefBaselineABCDtoRGB (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + const real32 *sPtrD, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB) + { + + real32 clipA = (real32) cameraWhite [0]; + real32 clipB = (real32) cameraWhite [1]; + real32 clipC = (real32) cameraWhite [2]; + real32 clipD = (real32) cameraWhite [3]; + + real32 m00 = (real32) cameraToRGB [0] [0]; + real32 m01 = (real32) cameraToRGB [0] [1]; + real32 m02 = (real32) cameraToRGB [0] [2]; + real32 m03 = (real32) cameraToRGB [0] [3]; + + real32 m10 = (real32) cameraToRGB [1] [0]; + real32 m11 = (real32) cameraToRGB [1] [1]; + real32 m12 = (real32) cameraToRGB [1] [2]; + real32 m13 = (real32) cameraToRGB [1] [3]; + + real32 m20 = (real32) cameraToRGB [2] [0]; + real32 m21 = (real32) cameraToRGB [2] [1]; + real32 m22 = (real32) cameraToRGB [2] [2]; + real32 m23 = (real32) cameraToRGB [2] [3]; + + for (uint32 col = 0; col < count; col++) + { + + real32 A = sPtrA [col]; + real32 B = sPtrB [col]; + real32 C = sPtrC [col]; + real32 D = sPtrD [col]; + + A = Min_real32 (A, clipA); + B = Min_real32 (B, clipB); + C = Min_real32 (C, clipC); + D = Min_real32 (D, clipD); + + real32 r = m00 * A + m01 * B + m02 * C + m03 * D; + real32 g = m10 * A + m11 * B + m12 * C + m13 * D; + real32 b = m20 * A + m21 * B + m22 * C + m23 * D; + + r = Pin_real32 (0.0f, r, 1.0f); + g = Pin_real32 (0.0f, g, 1.0f); + b = Pin_real32 (0.0f, b, 1.0f); + + dPtrR [col] = r; + dPtrG [col] = g; + dPtrB [col] = b; + + } + + } + +/*****************************************************************************/ + +void RefBaselineHueSatMap (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_hue_sat_map &lut, + const dng_1d_table *encodeTable, + const dng_1d_table *decodeTable) + { + + uint32 hueDivisions; + uint32 satDivisions; + uint32 valDivisions; + + lut.GetDivisions (hueDivisions, + satDivisions, + valDivisions); + + real32 hScale = (hueDivisions < 2) ? 0.0f : (hueDivisions * (1.0f / 6.0f)); + real32 sScale = (real32) (satDivisions - 1); + real32 vScale = (real32) (valDivisions - 1); + + int32 maxHueIndex0 = hueDivisions - 1; + int32 maxSatIndex0 = satDivisions - 2; + int32 maxValIndex0 = valDivisions - 2; + + const bool hasEncodeTable = ((encodeTable != NULL) && (encodeTable->Table () != NULL)); + const bool hasDecodeTable = ((decodeTable != NULL) && (decodeTable->Table () != NULL)); + + const bool hasTable = hasEncodeTable && hasDecodeTable; + + const dng_hue_sat_map::HSBModify *tableBase = lut.GetConstDeltas (); + + int32 hueStep = satDivisions; + int32 valStep = hueDivisions * hueStep; + + #if 0 // Not required with "2.5D" table optimization. + + if (valDivisions < 2) + { + valStep = 0; + maxValIndex0 = 0; + } + + #endif + + for (uint32 j = 0; j < count; j++) + { + + real32 r = sPtrR [j]; + real32 g = sPtrG [j]; + real32 b = sPtrB [j]; + + real32 h, s, v; + + DNG_RGBtoHSV (r, g, b, h, s, v); + + real32 vEncoded = v; + + real32 hueShift; + real32 satScale; + real32 valScale; + + if (valDivisions < 2) // Optimize most common case of "2.5D" table. + { + + real32 hScaled = h * hScale; + real32 sScaled = s * sScale; + + int32 hIndex0 = (int32) hScaled; + int32 sIndex0 = (int32) sScaled; + + sIndex0 = Min_int32 (sIndex0, maxSatIndex0); + + int32 hIndex1 = hIndex0 + 1; + + if (hIndex0 >= maxHueIndex0) + { + hIndex0 = maxHueIndex0; + hIndex1 = 0; + } + + real32 hFract1 = hScaled - (real32) hIndex0; + real32 sFract1 = sScaled - (real32) sIndex0; + + real32 hFract0 = 1.0f - hFract1; + real32 sFract0 = 1.0f - sFract1; + + const dng_hue_sat_map::HSBModify *entry00 = tableBase + hIndex0 * hueStep + + sIndex0; + + const dng_hue_sat_map::HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep; + + real32 hueShift0 = hFract0 * entry00->fHueShift + + hFract1 * entry01->fHueShift; + + real32 satScale0 = hFract0 * entry00->fSatScale + + hFract1 * entry01->fSatScale; + + real32 valScale0 = hFract0 * entry00->fValScale + + hFract1 * entry01->fValScale; + + entry00++; + entry01++; + + real32 hueShift1 = hFract0 * entry00->fHueShift + + hFract1 * entry01->fHueShift; + + real32 satScale1 = hFract0 * entry00->fSatScale + + hFract1 * entry01->fSatScale; + + real32 valScale1 = hFract0 * entry00->fValScale + + hFract1 * entry01->fValScale; + + hueShift = sFract0 * hueShift0 + sFract1 * hueShift1; + satScale = sFract0 * satScale0 + sFract1 * satScale1; + valScale = sFract0 * valScale0 + sFract1 * valScale1; + + } + + else + { + + if (hasTable) + { + vEncoded = encodeTable->Interpolate (Pin_real32 (v)); + } + + real32 hScaled = h * hScale; + real32 sScaled = s * sScale; + real32 vScaled = vEncoded * vScale; + + int32 hIndex0 = (int32) hScaled; + int32 sIndex0 = (int32) sScaled; + int32 vIndex0 = (int32) vScaled; + + sIndex0 = Min_int32 (sIndex0, maxSatIndex0); + vIndex0 = Min_int32 (vIndex0, maxValIndex0); + + int32 hIndex1 = hIndex0 + 1; + + if (hIndex0 >= maxHueIndex0) + { + hIndex0 = maxHueIndex0; + hIndex1 = 0; + } + + real32 hFract1 = hScaled - (real32) hIndex0; + real32 sFract1 = sScaled - (real32) sIndex0; + real32 vFract1 = vScaled - (real32) vIndex0; + + real32 hFract0 = 1.0f - hFract1; + real32 sFract0 = 1.0f - sFract1; + real32 vFract0 = 1.0f - vFract1; + + const dng_hue_sat_map::HSBModify *entry00 = tableBase + vIndex0 * valStep + + hIndex0 * hueStep + + sIndex0; + + const dng_hue_sat_map::HSBModify *entry01 = entry00 + (hIndex1 - hIndex0) * hueStep; + + const dng_hue_sat_map::HSBModify *entry10 = entry00 + valStep; + const dng_hue_sat_map::HSBModify *entry11 = entry01 + valStep; + + real32 hueShift0 = vFract0 * (hFract0 * entry00->fHueShift + + hFract1 * entry01->fHueShift) + + vFract1 * (hFract0 * entry10->fHueShift + + hFract1 * entry11->fHueShift); + + real32 satScale0 = vFract0 * (hFract0 * entry00->fSatScale + + hFract1 * entry01->fSatScale) + + vFract1 * (hFract0 * entry10->fSatScale + + hFract1 * entry11->fSatScale); + + real32 valScale0 = vFract0 * (hFract0 * entry00->fValScale + + hFract1 * entry01->fValScale) + + vFract1 * (hFract0 * entry10->fValScale + + hFract1 * entry11->fValScale); + + entry00++; + entry01++; + entry10++; + entry11++; + + real32 hueShift1 = vFract0 * (hFract0 * entry00->fHueShift + + hFract1 * entry01->fHueShift) + + vFract1 * (hFract0 * entry10->fHueShift + + hFract1 * entry11->fHueShift); + + real32 satScale1 = vFract0 * (hFract0 * entry00->fSatScale + + hFract1 * entry01->fSatScale) + + vFract1 * (hFract0 * entry10->fSatScale + + hFract1 * entry11->fSatScale); + + real32 valScale1 = vFract0 * (hFract0 * entry00->fValScale + + hFract1 * entry01->fValScale) + + vFract1 * (hFract0 * entry10->fValScale + + hFract1 * entry11->fValScale); + + hueShift = sFract0 * hueShift0 + sFract1 * hueShift1; + satScale = sFract0 * satScale0 + sFract1 * satScale1; + valScale = sFract0 * valScale0 + sFract1 * valScale1; + + } + + hueShift *= (6.0f / 360.0f); // Convert to internal hue range. + + h += hueShift; + + s = Min_real32 (s * satScale, 1.0f); + + vEncoded = Pin_real32 (vEncoded * valScale); + + v = hasTable ? decodeTable->Interpolate (vEncoded) : vEncoded; + + DNG_HSVtoRGB (h, s, v, r, g, b); + + dPtrR [j] = r; + dPtrG [j] = g; + dPtrB [j] = b; + + } + + } + +/*****************************************************************************/ + +void RefBaselineRGBtoGray (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrG, + uint32 count, + const dng_matrix &matrix) + { + + real32 m00 = (real32) matrix [0] [0]; + real32 m01 = (real32) matrix [0] [1]; + real32 m02 = (real32) matrix [0] [2]; + + for (uint32 col = 0; col < count; col++) + { + + real32 R = sPtrR [col]; + real32 G = sPtrG [col]; + real32 B = sPtrB [col]; + + real32 g = m00 * R + m01 * G + m02 * B; + + g = Pin_real32 (0.0f, g, 1.0f); + + dPtrG [col] = g; + + } + + } + +/*****************************************************************************/ + +void RefBaselineRGBtoRGB (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_matrix &matrix) + { + + real32 m00 = (real32) matrix [0] [0]; + real32 m01 = (real32) matrix [0] [1]; + real32 m02 = (real32) matrix [0] [2]; + + real32 m10 = (real32) matrix [1] [0]; + real32 m11 = (real32) matrix [1] [1]; + real32 m12 = (real32) matrix [1] [2]; + + real32 m20 = (real32) matrix [2] [0]; + real32 m21 = (real32) matrix [2] [1]; + real32 m22 = (real32) matrix [2] [2]; + + for (uint32 col = 0; col < count; col++) + { + + real32 R = sPtrR [col]; + real32 G = sPtrG [col]; + real32 B = sPtrB [col]; + + real32 r = m00 * R + m01 * G + m02 * B; + real32 g = m10 * R + m11 * G + m12 * B; + real32 b = m20 * R + m21 * G + m22 * B; + + r = Pin_real32 (0.0f, r, 1.0f); + g = Pin_real32 (0.0f, g, 1.0f); + b = Pin_real32 (0.0f, b, 1.0f); + + dPtrR [col] = r; + dPtrG [col] = g; + dPtrB [col] = b; + + } + + } + +/*****************************************************************************/ + +void RefBaseline1DTable (const real32 *sPtr, + real32 *dPtr, + uint32 count, + const dng_1d_table &table) + { + + for (uint32 col = 0; col < count; col++) + { + + real32 x = sPtr [col]; + + real32 y = table.Interpolate (x); + + dPtr [col] = y; + + } + + } + +/*****************************************************************************/ + +void RefBaselineRGBTone (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_1d_table &table) + { + + for (uint32 col = 0; col < count; col++) + { + + real32 r = sPtrR [col]; + real32 g = sPtrG [col]; + real32 b = sPtrB [col]; + + real32 rr; + real32 gg; + real32 bb; + + #define RGBTone(r, g, b, rr, gg, bb)\ + {\ + \ + DNG_ASSERT (r >= g && g >= b && r > b, "Logic Error RGBTone");\ + \ + rr = table.Interpolate (r);\ + bb = table.Interpolate (b);\ + \ + gg = bb + ((rr - bb) * (g - b) / (r - b));\ + \ + } + + if (r >= g) + { + + if (g > b) + { + + // Case 1: r >= g > b + + RGBTone (r, g, b, rr, gg, bb); + + } + + else if (b > r) + { + + // Case 2: b > r >= g + + RGBTone (b, r, g, bb, rr, gg); + + } + + else if (b > g) + { + + // Case 3: r >= b > g + + RGBTone (r, b, g, rr, bb, gg); + + } + + else + { + + // Case 4: r >= g == b + + DNG_ASSERT (r >= g && g == b, "Logic Error 2"); + + rr = table.Interpolate (r); + gg = table.Interpolate (g); + bb = gg; + + } + + } + + else + { + + if (r >= b) + { + + // Case 5: g > r >= b + + RGBTone (g, r, b, gg, rr, bb); + + } + + else if (b > g) + { + + // Case 6: b > g > r + + RGBTone (b, g, r, bb, gg, rr); + + } + + else + { + + // Case 7: g >= b > r + + RGBTone (g, b, r, gg, bb, rr); + + } + + } + + #undef RGBTone + + dPtrR [col] = rr; + dPtrG [col] = gg; + dPtrB [col] = bb; + + } + + } + +/*****************************************************************************/ + +void RefResampleDown16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 sCount, + int32 sRowStep, + const int16 *wPtr, + uint32 wCount, + uint32 pixelRange) + { + + for (uint32 j = 0; j < sCount; j++) + { + + int32 total = 8192; + + const uint16 *s = sPtr + j; + + for (uint32 k = 0; k < wCount; k++) + { + + total += wPtr [k] * (int32) s [0]; + + s += sRowStep; + + } + + dPtr [j] = (uint16) Pin_int32 (0, + total >> 14, + pixelRange); + + } + + } + +/*****************************************************************************/ + +void RefResampleDown32 (const real32 *sPtr, + real32 *dPtr, + uint32 sCount, + int32 sRowStep, + const real32 *wPtr, + uint32 wCount) + { + + uint32 col; + + // Process first row. + + real32 w = wPtr [0]; + + for (col = 0; col < sCount; col++) + { + + dPtr [col] = w * sPtr [col]; + + } + + sPtr += sRowStep; + + // Process middle rows. + + for (uint32 j = 1; j < wCount - 1; j++) + { + + w = wPtr [j]; + + for (col = 0; col < sCount; col++) + { + + dPtr [col] += w * sPtr [col]; + + } + + sPtr += sRowStep; + + } + + // Process last row. + + w = wPtr [wCount - 1]; + + for (col = 0; col < sCount; col++) + { + + dPtr [col] = Pin_real32 (0.0f, + dPtr [col] + w * sPtr [col], + 1.0f); + + } + + } + +/******************************************************************************/ + +void RefResampleAcross16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 dCount, + const int32 *coord, + const int16 *wPtr, + uint32 wCount, + uint32 wStep, + uint32 pixelRange) + { + + for (uint32 j = 0; j < dCount; j++) + { + + int32 sCoord = coord [j]; + + int32 sFract = sCoord & kResampleSubsampleMask; + int32 sPixel = sCoord >> kResampleSubsampleBits; + + const int16 *w = wPtr + sFract * wStep; + const uint16 *s = sPtr + sPixel; + + int32 total = w [0] * (int32) s [0]; + + for (uint32 k = 1; k < wCount; k++) + { + + total += w [k] * (int32) s [k]; + + } + + dPtr [j] = (uint16) Pin_int32 (0, + (total + 8192) >> 14, + pixelRange); + + } + + } + +/******************************************************************************/ + +void RefResampleAcross32 (const real32 *sPtr, + real32 *dPtr, + uint32 dCount, + const int32 *coord, + const real32 *wPtr, + uint32 wCount, + uint32 wStep) + { + + for (uint32 j = 0; j < dCount; j++) + { + + int32 sCoord = coord [j]; + + int32 sFract = sCoord & kResampleSubsampleMask; + int32 sPixel = sCoord >> kResampleSubsampleBits; + + const real32 *w = wPtr + sFract * wStep; + const real32 *s = sPtr + sPixel; + + real32 total = w [0] * s [0]; + + for (uint32 k = 1; k < wCount; k++) + { + + total += w [k] * s [k]; + + } + + dPtr [j] = Pin_real32 (0.0f, total, 1.0f); + + } + + } + +/*****************************************************************************/ + +bool RefEqualBytes (const void *sPtr, + const void *dPtr, + uint32 count) + { + + return memcmp (dPtr, sPtr, count) == 0; + + } + +/*****************************************************************************/ + +bool RefEqualArea8 (const uint8 *sPtr, + const uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint8 *sPtr1 = sPtr; + const uint8 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint8 *sPtr2 = sPtr1; + const uint8 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + if (*dPtr2 != *sPtr2) + return false; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + return true; + + } + +/*****************************************************************************/ + +bool RefEqualArea16 (const uint16 *sPtr, + const uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint16 *sPtr1 = sPtr; + const uint16 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint16 *sPtr2 = sPtr1; + const uint16 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + if (*dPtr2 != *sPtr2) + return false; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + return true; + + } + +/*****************************************************************************/ + +bool RefEqualArea32 (const uint32 *sPtr, + const uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep) + { + + for (uint32 row = 0; row < rows; row++) + { + + const uint32 *sPtr1 = sPtr; + const uint32 *dPtr1 = dPtr; + + for (uint32 col = 0; col < cols; col++) + { + + const uint32 *sPtr2 = sPtr1; + const uint32 *dPtr2 = dPtr1; + + for (uint32 plane = 0; plane < planes; plane++) + { + + if (*dPtr2 != *sPtr2) + return false; + + sPtr2 += sPlaneStep; + dPtr2 += dPlaneStep; + + } + + sPtr1 += sColStep; + dPtr1 += dColStep; + + } + + sPtr += sRowStep; + dPtr += dRowStep; + + } + + return true; + + } + +/*****************************************************************************/ + +void RefVignetteMask16 (uint16 *mPtr, + uint32 rows, + uint32 cols, + int32 rowStep, + int64 offsetH, + int64 offsetV, + int64 stepH, + int64 stepV, + uint32 tBits, + const uint16 *table) + { + + uint32 tShift = 32 - tBits; + uint32 tRound = (1 << (tShift - 1)); + uint32 tLimit = 1 << tBits; + + for (uint32 row = 0; row < rows; row++) + { + + int64 baseDelta = (offsetV + 32768) >> 16; + + baseDelta = baseDelta * baseDelta + tRound; + + int64 deltaH = offsetH + 32768; + + for (uint32 col = 0; col < cols; col++) + { + + int64 temp = deltaH >> 16; + + int64 delta = baseDelta + (temp * temp); + + uint32 index = Min_uint32 ((uint32) (delta >> tShift), tLimit); + + mPtr [col] = table [index]; + + deltaH += stepH; + + } + + offsetV += stepV; + + mPtr += rowStep; + + } + + } + +/*****************************************************************************/ + +void RefVignette16 (int16 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits) + { + + const uint32 mRound = 1 << (mBits - 1); + + switch (planes) + { + + case 1: + { + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + uint32 s = sPtr [col] + 32768; + + uint32 m = mPtr [col]; + + s = (s * m + mRound) >> mBits; + + s = Min_uint32 (s, 65535); + + sPtr [col] = (int16) (s - 32768); + + } + + sPtr += sRowStep; + + mPtr += mRowStep; + + } + + break; + + } + + case 3: + { + + int16 *rPtr = sPtr; + int16 *gPtr = rPtr + sPlaneStep; + int16 *bPtr = gPtr + sPlaneStep; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + uint32 r = rPtr [col] + 32768; + uint32 g = gPtr [col] + 32768; + uint32 b = bPtr [col] + 32768; + + uint32 m = mPtr [col]; + + r = (r * m + mRound) >> mBits; + g = (g * m + mRound) >> mBits; + b = (b * m + mRound) >> mBits; + + r = Min_uint32 (r, 65535); + g = Min_uint32 (g, 65535); + b = Min_uint32 (b, 65535); + + rPtr [col] = (int16) (r - 32768); + gPtr [col] = (int16) (g - 32768); + bPtr [col] = (int16) (b - 32768); + + } + + rPtr += sRowStep; + gPtr += sRowStep; + bPtr += sRowStep; + + mPtr += mRowStep; + + } + + break; + + } + + case 4: + { + + int16 *aPtr = sPtr; + int16 *bPtr = aPtr + sPlaneStep; + int16 *cPtr = bPtr + sPlaneStep; + int16 *dPtr = cPtr + sPlaneStep; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + uint32 a = aPtr [col] + 32768; + uint32 b = bPtr [col] + 32768; + uint32 c = cPtr [col] + 32768; + uint32 d = dPtr [col] + 32768; + + uint32 m = mPtr [col]; + + a = (a * m + mRound) >> mBits; + b = (b * m + mRound) >> mBits; + c = (c * m + mRound) >> mBits; + d = (d * m + mRound) >> mBits; + + a = Min_uint32 (a, 65535); + b = Min_uint32 (b, 65535); + c = Min_uint32 (c, 65535); + d = Min_uint32 (d, 65535); + + aPtr [col] = (int16) (a - 32768); + bPtr [col] = (int16) (b - 32768); + cPtr [col] = (int16) (c - 32768); + dPtr [col] = (int16) (d - 32768); + + } + + aPtr += sRowStep; + bPtr += sRowStep; + cPtr += sRowStep; + dPtr += sRowStep; + + mPtr += mRowStep; + + } + + break; + + } + + default: + { + + for (uint32 plane = 0; plane < planes; plane++) + { + + int16 *planePtr = sPtr; + + const uint16 *maskPtr = mPtr; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + uint32 s = planePtr [col] + 32768; + + uint32 m = maskPtr [col]; + + s = (s * m + mRound) >> mBits; + + s = Min_uint32 (s, 65535); + + planePtr [col] = (int16) (s - 32768); + + } + + planePtr += sRowStep; + + maskPtr += mRowStep; + + } + + sPtr += sPlaneStep; + + } + + break; + + } + + } + + } + +/*****************************************************************************/ + +void RefVignette32 (real32 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits) + { + + const real32 kNorm = 1.0f / (1 << mBits); + + switch (planes) + { + + case 1: + { + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + real32 s = sPtr [col]; + + uint16 m = mPtr [col]; + + real32 scale = m * kNorm; + + s = Min_real32 (s * scale, 1.0f); + + sPtr [col] = s; + + } + + sPtr += sRowStep; + + mPtr += mRowStep; + + } + + break; + + } + + case 3: + { + + real32 *rPtr = sPtr; + real32 *gPtr = rPtr + sPlaneStep; + real32 *bPtr = gPtr + sPlaneStep; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + real32 r = rPtr [col]; + real32 g = gPtr [col]; + real32 b = bPtr [col]; + + uint16 m = mPtr [col]; + + real32 scale = m * kNorm; + + r = Min_real32 (r * scale, 1.0f); + g = Min_real32 (g * scale, 1.0f); + b = Min_real32 (b * scale, 1.0f); + + rPtr [col] = r; + gPtr [col] = g; + bPtr [col] = b; + + } + + rPtr += sRowStep; + gPtr += sRowStep; + bPtr += sRowStep; + + mPtr += mRowStep; + + } + + break; + + } + + case 4: + { + + real32 *aPtr = sPtr; + real32 *bPtr = aPtr + sPlaneStep; + real32 *cPtr = bPtr + sPlaneStep; + real32 *dPtr = cPtr + sPlaneStep; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + real32 a = aPtr [col]; + real32 b = bPtr [col]; + real32 c = cPtr [col]; + real32 d = dPtr [col]; + + uint16 m = mPtr [col]; + + real32 scale = m * kNorm; + + a = Min_real32 (a * scale, 1.0f); + b = Min_real32 (b * scale, 1.0f); + c = Min_real32 (c * scale, 1.0f); + d = Min_real32 (d * scale, 1.0f); + + aPtr [col] = a; + bPtr [col] = b; + cPtr [col] = c; + dPtr [col] = d; + + } + + aPtr += sRowStep; + bPtr += sRowStep; + cPtr += sRowStep; + dPtr += sRowStep; + + mPtr += mRowStep; + + } + + break; + + } + + default: + { + + for (uint32 plane = 0; plane < planes; plane++) + { + + real32 *planePtr = sPtr; + + const uint16 *maskPtr = mPtr; + + for (uint32 row = 0; row < rows; row++) + { + + for (uint32 col = 0; col < cols; col++) + { + + real32 s = planePtr [col]; + + uint16 m = maskPtr [col]; + + real32 scale = m * kNorm; + + s = Min_real32 (s * scale, 1.0f); + + planePtr [col] = s; + + } + + planePtr += sRowStep; + + maskPtr += mRowStep; + + } + + sPtr += sPlaneStep; + + } + + break; + + } + + } + + } + +/******************************************************************************/ + +void RefMapArea16 (uint16 *dPtr, + uint32 count0, + uint32 count1, + uint32 count2, + int32 step0, + int32 step1, + int32 step2, + const uint16 *map) + { + + if (step2 == 1 && count2 >= 32) + { + + for (uint32 index0 = 0; index0 < count0; index0++) + { + + uint16 *d1 = dPtr; + + for (uint32 index1 = 0; index1 < count1; index1++) + { + + uint16 *d2 = d1; + + uint32 count = count2; + + // Get the data 32-bit aligned if it is not. + + if (!IsAligned32 (dPtr)) + { + + d2 [0] = map [d2 [0]]; + + count--; + + d2++; + + } + + // Use 32-bit reads and writes for bulk processing. + + uint32 *dPtr32 = (uint32 *) d2; + + // Process in blocks of 16 pixels. + + uint32 blocks = count >> 4; + + count -= blocks << 4; + d2 += blocks << 4; + + while (blocks--) + { + + uint32 x0, x1, x2, x3, x4, x5, x6, x7; + uint32 p0, p1, p2, p3, p4, p5, p6, p7; + + // Use 32 bit reads & writes, and pack and unpack the 16-bit values. + // This results in slightly higher performance. + + // Note that this code runs on both little-endian and big-endian systems, + // since the pixels are either never swapped or double swapped. + + x0 = dPtr32 [0]; + x1 = dPtr32 [1]; + x2 = dPtr32 [2]; + x3 = dPtr32 [3]; + + p0 = map [x0 >> 16 ]; + p1 = map [x0 & 0x0FFFF]; + p2 = map [x1 >> 16 ]; + p3 = map [x1 & 0x0FFFF]; + p4 = map [x2 >> 16 ]; + p5 = map [x2 & 0x0FFFF]; + p6 = map [x3 >> 16 ]; + p7 = map [x3 & 0x0FFFF]; + + x0 = (p0 << 16) | p1; + x1 = (p2 << 16) | p3; + x2 = (p4 << 16) | p5; + x3 = (p6 << 16) | p7; + + x4 = dPtr32 [4]; + x5 = dPtr32 [5]; + x6 = dPtr32 [6]; + x7 = dPtr32 [7]; + + dPtr32 [0] = x0; + dPtr32 [1] = x1; + dPtr32 [2] = x2; + dPtr32 [3] = x3; + + p0 = map [x4 >> 16 ]; + p1 = map [x4 & 0x0FFFF]; + p2 = map [x5 >> 16 ]; + p3 = map [x5 & 0x0FFFF]; + p4 = map [x6 >> 16 ]; + p5 = map [x6 & 0x0FFFF]; + p6 = map [x7 >> 16 ]; + p7 = map [x7 & 0x0FFFF]; + + x4 = (p0 << 16) | p1; + x5 = (p2 << 16) | p3; + x6 = (p4 << 16) | p5; + x7 = (p6 << 16) | p7; + + dPtr32 [4] = x4; + dPtr32 [5] = x5; + dPtr32 [6] = x6; + dPtr32 [7] = x7; + + dPtr32 += 8; + + } + + // Process remaining columns. + + for (uint32 j = 0; j < count; j++) + { + + d2 [j] = map [d2 [j]]; + + } + + d1 += step1; + + } + + dPtr += step0; + + } + + } + + else + { + + for (uint32 index0 = 0; index0 < count0; index0++) + { + + uint16 *d1 = dPtr; + + for (uint32 index1 = 0; index1 < count1; index1++) + { + + uint16 *d2 = d1; + + for (uint32 index2 = 0; index2 < count2; index2++) + { + + d2 [0] = map [d2 [0]]; + + d2 += step2; + + } + + d1 += step1; + + } + + dPtr += step0; + + } + + } + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_reference.h b/source/lib/dng_sdk/dng_reference.h new file mode 100644 index 0000000..d75dcec --- /dev/null +++ b/source/lib/dng_sdk/dng_reference.h @@ -0,0 +1,521 @@ +/*****************************************************************************/ +// Copyright 2006-2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_reference.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_reference__ +#define __dng_reference__ + +/*****************************************************************************/ + +#include "dng_bottlenecks.h" + +/*****************************************************************************/ + +void RefZeroBytes (void *dPtr, + uint32 count); + +void RefCopyBytes (const void *sPtr, + void *dPtr, + uint32 count); + +/*****************************************************************************/ + +void RefSwapBytes16 (uint16 *dPtr, + uint32 count); + +void RefSwapBytes32 (uint32 *dPtr, + uint32 count); + +/*****************************************************************************/ + +void RefSetArea8 (uint8 *dPtr, + uint8 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); + +void RefSetArea16 (uint16 *dPtr, + uint16 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); + +void RefSetArea32 (uint32 *dPtr, + uint32 value, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep); + +/*****************************************************************************/ + +void RefCopyArea8 (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea32 (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea8_16 (const uint8 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea8_S16 (const uint8 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea8_32 (const uint8 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea16_S16 (const uint16 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea16_32 (const uint16 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +void RefCopyArea8_R32 (const uint8 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +void RefCopyArea16_R32 (const uint16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +void RefCopyAreaS16_R32 (const int16 *sPtr, + real32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +void RefCopyAreaR32_8 (const real32 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +void RefCopyAreaR32_16 (const real32 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +void RefCopyAreaR32_S16 (const real32 *sPtr, + int16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep, + uint32 pixelRange); + +/*****************************************************************************/ + +void RefRepeatArea8 (const uint8 *sPtr, + uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH); + +void RefRepeatArea16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH); + +void RefRepeatArea32 (const uint32 *sPtr, + uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 repeatV, + uint32 repeatH, + uint32 phaseV, + uint32 phaseH); + +/*****************************************************************************/ + +void RefShiftRight16 (uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 rowStep, + int32 colStep, + int32 planeStep, + uint32 shift); + +/*****************************************************************************/ + +void RefBilinearRow16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const uint16 * const * kernWeights, + uint32 sShift); + +void RefBilinearRow32 (const real32 *sPtr, + real32 *dPtr, + uint32 cols, + uint32 patPhase, + uint32 patCount, + const uint32 * kernCounts, + const int32 * const * kernOffsets, + const real32 * const * kernWeights, + uint32 sShift); + +/*****************************************************************************/ + +void RefBaselineABCtoRGB (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB); + +void RefBaselineABCDtoRGB (const real32 *sPtrA, + const real32 *sPtrB, + const real32 *sPtrC, + const real32 *sPtrD, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_vector &cameraWhite, + const dng_matrix &cameraToRGB); + +/*****************************************************************************/ + +void RefBaselineHueSatMap (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_hue_sat_map &lut, + const dng_1d_table *encodeTable, + const dng_1d_table *decodeTable); + +/*****************************************************************************/ + +void RefBaselineRGBtoGray (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrG, + uint32 count, + const dng_matrix &matrix); + +void RefBaselineRGBtoRGB (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_matrix &matrix); + +/*****************************************************************************/ + +void RefBaseline1DTable (const real32 *sPtr, + real32 *dPtr, + uint32 count, + const dng_1d_table &table); + +/*****************************************************************************/ + +void RefBaselineRGBTone (const real32 *sPtrR, + const real32 *sPtrG, + const real32 *sPtrB, + real32 *dPtrR, + real32 *dPtrG, + real32 *dPtrB, + uint32 count, + const dng_1d_table &table); + +/*****************************************************************************/ + +void RefResampleDown16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 sCount, + int32 sRowStep, + const int16 *wPtr, + uint32 wCount, + uint32 pixelRange); + +void RefResampleDown32 (const real32 *sPtr, + real32 *dPtr, + uint32 sCount, + int32 sRowStep, + const real32 *wPtr, + uint32 wCount); + +/*****************************************************************************/ + +void RefResampleAcross16 (const uint16 *sPtr, + uint16 *dPtr, + uint32 dCount, + const int32 *coord, + const int16 *wPtr, + uint32 wCount, + uint32 wStep, + uint32 pixelRange); + +void RefResampleAcross32 (const real32 *sPtr, + real32 *dPtr, + uint32 dCount, + const int32 *coord, + const real32 *wPtr, + uint32 wCount, + uint32 wStep); + +/*****************************************************************************/ + +bool RefEqualBytes (const void *sPtr, + const void *dPtr, + uint32 count); + +bool RefEqualArea8 (const uint8 *sPtr, + const uint8 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +bool RefEqualArea16 (const uint16 *sPtr, + const uint16 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +bool RefEqualArea32 (const uint32 *sPtr, + const uint32 *dPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sColStep, + int32 sPlaneStep, + int32 dRowStep, + int32 dColStep, + int32 dPlaneStep); + +/*****************************************************************************/ + +void RefVignetteMask16 (uint16 *mPtr, + uint32 rows, + uint32 cols, + int32 rowStep, + int64 offsetH, + int64 offsetV, + int64 stepH, + int64 stepV, + uint32 tBits, + const uint16 *table); + +/*****************************************************************************/ + +void RefVignette16 (int16 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits); + +/*****************************************************************************/ + +void RefVignette32 (real32 *sPtr, + const uint16 *mPtr, + uint32 rows, + uint32 cols, + uint32 planes, + int32 sRowStep, + int32 sPlaneStep, + int32 mRowStep, + uint32 mBits); + +/*****************************************************************************/ + +void RefMapArea16 (uint16 *dPtr, + uint32 count0, + uint32 count1, + uint32 count2, + int32 step0, + int32 step1, + int32 step2, + const uint16 *map); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_render.cpp b/source/lib/dng_sdk/dng_render.cpp new file mode 100644 index 0000000..1751363 --- /dev/null +++ b/source/lib/dng_sdk/dng_render.cpp @@ -0,0 +1,1328 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_render.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_render.h" + +#include "dng_1d_table.h" +#include "dng_bottlenecks.h" +#include "dng_camera_profile.h" +#include "dng_color_space.h" +#include "dng_color_spec.h" +#include "dng_filter_task.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_negative.h" +#include "dng_resample.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_function_exposure_ramp::dng_function_exposure_ramp (real64 white, + real64 black, + real64 minBlack) + + : fSlope (1.0 / (white - black)) + , fBlack (black) + + , fRadius (0.0) + , fQScale (0.0) + + { + + const real64 kMaxCurveX = 0.5; // Fraction of minBlack. + + const real64 kMaxCurveY = 1.0 / 16.0; // Fraction of white. + + fRadius = Min_real64 (kMaxCurveX * minBlack, + kMaxCurveY / fSlope); + + if (fRadius > 0.0) + fQScale= fSlope / (4.0 * fRadius); + else + fQScale = 0.0; + + } + +/*****************************************************************************/ + +real64 dng_function_exposure_ramp::Evaluate (real64 x) const + { + + if (x <= fBlack - fRadius) + return 0.0; + + if (x >= fBlack + fRadius) + return Min_real64 ((x - fBlack) * fSlope, 1.0); + + real64 y = x - (fBlack - fRadius); + + return fQScale * y * y; + + } + +/*****************************************************************************/ + +dng_function_exposure_tone::dng_function_exposure_tone (real64 exposure) + + : fIsNOP (exposure >= 0.0) + + , fSlope (0.0) + + , a (0.0) + , b (0.0) + , c (0.0) + + { + + if (!fIsNOP) + { + + // Find slope to use for the all except the highest two f-stops. + + fSlope = pow (2.0, exposure); + + // Find quadradic parameters that match this darking at the crossover + // point, yet still map pure white to pure white. + + a = 16.0 / 9.0 * (1.0 - fSlope); + + b = fSlope - 0.5 * a; + + c = 1.0 - a - b; + + } + + } + +/*****************************************************************************/ + +real64 dng_function_exposure_tone::Evaluate (real64 x) const + { + + if (!fIsNOP) + { + + if (x <= 0.25) + x = x * fSlope; + + else + x = (a * x + b) * x + c; + + } + + return x; + + } + +/*****************************************************************************/ + +real64 dng_tone_curve_acr3_default::Evaluate (real64 x) const + { + + static const real32 kTable [] = + { + 0.00000f, 0.00078f, 0.00160f, 0.00242f, + 0.00314f, 0.00385f, 0.00460f, 0.00539f, + 0.00623f, 0.00712f, 0.00806f, 0.00906f, + 0.01012f, 0.01122f, 0.01238f, 0.01359f, + 0.01485f, 0.01616f, 0.01751f, 0.01890f, + 0.02033f, 0.02180f, 0.02331f, 0.02485f, + 0.02643f, 0.02804f, 0.02967f, 0.03134f, + 0.03303f, 0.03475f, 0.03648f, 0.03824f, + 0.04002f, 0.04181f, 0.04362f, 0.04545f, + 0.04730f, 0.04916f, 0.05103f, 0.05292f, + 0.05483f, 0.05675f, 0.05868f, 0.06063f, + 0.06259f, 0.06457f, 0.06655f, 0.06856f, + 0.07057f, 0.07259f, 0.07463f, 0.07668f, + 0.07874f, 0.08081f, 0.08290f, 0.08499f, + 0.08710f, 0.08921f, 0.09134f, 0.09348f, + 0.09563f, 0.09779f, 0.09996f, 0.10214f, + 0.10433f, 0.10652f, 0.10873f, 0.11095f, + 0.11318f, 0.11541f, 0.11766f, 0.11991f, + 0.12218f, 0.12445f, 0.12673f, 0.12902f, + 0.13132f, 0.13363f, 0.13595f, 0.13827f, + 0.14061f, 0.14295f, 0.14530f, 0.14765f, + 0.15002f, 0.15239f, 0.15477f, 0.15716f, + 0.15956f, 0.16197f, 0.16438f, 0.16680f, + 0.16923f, 0.17166f, 0.17410f, 0.17655f, + 0.17901f, 0.18148f, 0.18395f, 0.18643f, + 0.18891f, 0.19141f, 0.19391f, 0.19641f, + 0.19893f, 0.20145f, 0.20398f, 0.20651f, + 0.20905f, 0.21160f, 0.21416f, 0.21672f, + 0.21929f, 0.22185f, 0.22440f, 0.22696f, + 0.22950f, 0.23204f, 0.23458f, 0.23711f, + 0.23963f, 0.24215f, 0.24466f, 0.24717f, + 0.24967f, 0.25216f, 0.25465f, 0.25713f, + 0.25961f, 0.26208f, 0.26454f, 0.26700f, + 0.26945f, 0.27189f, 0.27433f, 0.27676f, + 0.27918f, 0.28160f, 0.28401f, 0.28641f, + 0.28881f, 0.29120f, 0.29358f, 0.29596f, + 0.29833f, 0.30069f, 0.30305f, 0.30540f, + 0.30774f, 0.31008f, 0.31241f, 0.31473f, + 0.31704f, 0.31935f, 0.32165f, 0.32395f, + 0.32623f, 0.32851f, 0.33079f, 0.33305f, + 0.33531f, 0.33756f, 0.33981f, 0.34205f, + 0.34428f, 0.34650f, 0.34872f, 0.35093f, + 0.35313f, 0.35532f, 0.35751f, 0.35969f, + 0.36187f, 0.36404f, 0.36620f, 0.36835f, + 0.37050f, 0.37264f, 0.37477f, 0.37689f, + 0.37901f, 0.38112f, 0.38323f, 0.38533f, + 0.38742f, 0.38950f, 0.39158f, 0.39365f, + 0.39571f, 0.39777f, 0.39982f, 0.40186f, + 0.40389f, 0.40592f, 0.40794f, 0.40996f, + 0.41197f, 0.41397f, 0.41596f, 0.41795f, + 0.41993f, 0.42191f, 0.42388f, 0.42584f, + 0.42779f, 0.42974f, 0.43168f, 0.43362f, + 0.43554f, 0.43747f, 0.43938f, 0.44129f, + 0.44319f, 0.44509f, 0.44698f, 0.44886f, + 0.45073f, 0.45260f, 0.45447f, 0.45632f, + 0.45817f, 0.46002f, 0.46186f, 0.46369f, + 0.46551f, 0.46733f, 0.46914f, 0.47095f, + 0.47275f, 0.47454f, 0.47633f, 0.47811f, + 0.47989f, 0.48166f, 0.48342f, 0.48518f, + 0.48693f, 0.48867f, 0.49041f, 0.49214f, + 0.49387f, 0.49559f, 0.49730f, 0.49901f, + 0.50072f, 0.50241f, 0.50410f, 0.50579f, + 0.50747f, 0.50914f, 0.51081f, 0.51247f, + 0.51413f, 0.51578f, 0.51742f, 0.51906f, + 0.52069f, 0.52232f, 0.52394f, 0.52556f, + 0.52717f, 0.52878f, 0.53038f, 0.53197f, + 0.53356f, 0.53514f, 0.53672f, 0.53829f, + 0.53986f, 0.54142f, 0.54297f, 0.54452f, + 0.54607f, 0.54761f, 0.54914f, 0.55067f, + 0.55220f, 0.55371f, 0.55523f, 0.55673f, + 0.55824f, 0.55973f, 0.56123f, 0.56271f, + 0.56420f, 0.56567f, 0.56715f, 0.56861f, + 0.57007f, 0.57153f, 0.57298f, 0.57443f, + 0.57587f, 0.57731f, 0.57874f, 0.58017f, + 0.58159f, 0.58301f, 0.58443f, 0.58583f, + 0.58724f, 0.58864f, 0.59003f, 0.59142f, + 0.59281f, 0.59419f, 0.59556f, 0.59694f, + 0.59830f, 0.59966f, 0.60102f, 0.60238f, + 0.60373f, 0.60507f, 0.60641f, 0.60775f, + 0.60908f, 0.61040f, 0.61173f, 0.61305f, + 0.61436f, 0.61567f, 0.61698f, 0.61828f, + 0.61957f, 0.62087f, 0.62216f, 0.62344f, + 0.62472f, 0.62600f, 0.62727f, 0.62854f, + 0.62980f, 0.63106f, 0.63232f, 0.63357f, + 0.63482f, 0.63606f, 0.63730f, 0.63854f, + 0.63977f, 0.64100f, 0.64222f, 0.64344f, + 0.64466f, 0.64587f, 0.64708f, 0.64829f, + 0.64949f, 0.65069f, 0.65188f, 0.65307f, + 0.65426f, 0.65544f, 0.65662f, 0.65779f, + 0.65897f, 0.66013f, 0.66130f, 0.66246f, + 0.66362f, 0.66477f, 0.66592f, 0.66707f, + 0.66821f, 0.66935f, 0.67048f, 0.67162f, + 0.67275f, 0.67387f, 0.67499f, 0.67611f, + 0.67723f, 0.67834f, 0.67945f, 0.68055f, + 0.68165f, 0.68275f, 0.68385f, 0.68494f, + 0.68603f, 0.68711f, 0.68819f, 0.68927f, + 0.69035f, 0.69142f, 0.69249f, 0.69355f, + 0.69461f, 0.69567f, 0.69673f, 0.69778f, + 0.69883f, 0.69988f, 0.70092f, 0.70196f, + 0.70300f, 0.70403f, 0.70506f, 0.70609f, + 0.70711f, 0.70813f, 0.70915f, 0.71017f, + 0.71118f, 0.71219f, 0.71319f, 0.71420f, + 0.71520f, 0.71620f, 0.71719f, 0.71818f, + 0.71917f, 0.72016f, 0.72114f, 0.72212f, + 0.72309f, 0.72407f, 0.72504f, 0.72601f, + 0.72697f, 0.72794f, 0.72890f, 0.72985f, + 0.73081f, 0.73176f, 0.73271f, 0.73365f, + 0.73460f, 0.73554f, 0.73647f, 0.73741f, + 0.73834f, 0.73927f, 0.74020f, 0.74112f, + 0.74204f, 0.74296f, 0.74388f, 0.74479f, + 0.74570f, 0.74661f, 0.74751f, 0.74842f, + 0.74932f, 0.75021f, 0.75111f, 0.75200f, + 0.75289f, 0.75378f, 0.75466f, 0.75555f, + 0.75643f, 0.75730f, 0.75818f, 0.75905f, + 0.75992f, 0.76079f, 0.76165f, 0.76251f, + 0.76337f, 0.76423f, 0.76508f, 0.76594f, + 0.76679f, 0.76763f, 0.76848f, 0.76932f, + 0.77016f, 0.77100f, 0.77183f, 0.77267f, + 0.77350f, 0.77432f, 0.77515f, 0.77597f, + 0.77680f, 0.77761f, 0.77843f, 0.77924f, + 0.78006f, 0.78087f, 0.78167f, 0.78248f, + 0.78328f, 0.78408f, 0.78488f, 0.78568f, + 0.78647f, 0.78726f, 0.78805f, 0.78884f, + 0.78962f, 0.79040f, 0.79118f, 0.79196f, + 0.79274f, 0.79351f, 0.79428f, 0.79505f, + 0.79582f, 0.79658f, 0.79735f, 0.79811f, + 0.79887f, 0.79962f, 0.80038f, 0.80113f, + 0.80188f, 0.80263f, 0.80337f, 0.80412f, + 0.80486f, 0.80560f, 0.80634f, 0.80707f, + 0.80780f, 0.80854f, 0.80926f, 0.80999f, + 0.81072f, 0.81144f, 0.81216f, 0.81288f, + 0.81360f, 0.81431f, 0.81503f, 0.81574f, + 0.81645f, 0.81715f, 0.81786f, 0.81856f, + 0.81926f, 0.81996f, 0.82066f, 0.82135f, + 0.82205f, 0.82274f, 0.82343f, 0.82412f, + 0.82480f, 0.82549f, 0.82617f, 0.82685f, + 0.82753f, 0.82820f, 0.82888f, 0.82955f, + 0.83022f, 0.83089f, 0.83155f, 0.83222f, + 0.83288f, 0.83354f, 0.83420f, 0.83486f, + 0.83552f, 0.83617f, 0.83682f, 0.83747f, + 0.83812f, 0.83877f, 0.83941f, 0.84005f, + 0.84069f, 0.84133f, 0.84197f, 0.84261f, + 0.84324f, 0.84387f, 0.84450f, 0.84513f, + 0.84576f, 0.84639f, 0.84701f, 0.84763f, + 0.84825f, 0.84887f, 0.84949f, 0.85010f, + 0.85071f, 0.85132f, 0.85193f, 0.85254f, + 0.85315f, 0.85375f, 0.85436f, 0.85496f, + 0.85556f, 0.85615f, 0.85675f, 0.85735f, + 0.85794f, 0.85853f, 0.85912f, 0.85971f, + 0.86029f, 0.86088f, 0.86146f, 0.86204f, + 0.86262f, 0.86320f, 0.86378f, 0.86435f, + 0.86493f, 0.86550f, 0.86607f, 0.86664f, + 0.86720f, 0.86777f, 0.86833f, 0.86889f, + 0.86945f, 0.87001f, 0.87057f, 0.87113f, + 0.87168f, 0.87223f, 0.87278f, 0.87333f, + 0.87388f, 0.87443f, 0.87497f, 0.87552f, + 0.87606f, 0.87660f, 0.87714f, 0.87768f, + 0.87821f, 0.87875f, 0.87928f, 0.87981f, + 0.88034f, 0.88087f, 0.88140f, 0.88192f, + 0.88244f, 0.88297f, 0.88349f, 0.88401f, + 0.88453f, 0.88504f, 0.88556f, 0.88607f, + 0.88658f, 0.88709f, 0.88760f, 0.88811f, + 0.88862f, 0.88912f, 0.88963f, 0.89013f, + 0.89063f, 0.89113f, 0.89163f, 0.89212f, + 0.89262f, 0.89311f, 0.89360f, 0.89409f, + 0.89458f, 0.89507f, 0.89556f, 0.89604f, + 0.89653f, 0.89701f, 0.89749f, 0.89797f, + 0.89845f, 0.89892f, 0.89940f, 0.89987f, + 0.90035f, 0.90082f, 0.90129f, 0.90176f, + 0.90222f, 0.90269f, 0.90316f, 0.90362f, + 0.90408f, 0.90454f, 0.90500f, 0.90546f, + 0.90592f, 0.90637f, 0.90683f, 0.90728f, + 0.90773f, 0.90818f, 0.90863f, 0.90908f, + 0.90952f, 0.90997f, 0.91041f, 0.91085f, + 0.91130f, 0.91173f, 0.91217f, 0.91261f, + 0.91305f, 0.91348f, 0.91392f, 0.91435f, + 0.91478f, 0.91521f, 0.91564f, 0.91606f, + 0.91649f, 0.91691f, 0.91734f, 0.91776f, + 0.91818f, 0.91860f, 0.91902f, 0.91944f, + 0.91985f, 0.92027f, 0.92068f, 0.92109f, + 0.92150f, 0.92191f, 0.92232f, 0.92273f, + 0.92314f, 0.92354f, 0.92395f, 0.92435f, + 0.92475f, 0.92515f, 0.92555f, 0.92595f, + 0.92634f, 0.92674f, 0.92713f, 0.92753f, + 0.92792f, 0.92831f, 0.92870f, 0.92909f, + 0.92947f, 0.92986f, 0.93025f, 0.93063f, + 0.93101f, 0.93139f, 0.93177f, 0.93215f, + 0.93253f, 0.93291f, 0.93328f, 0.93366f, + 0.93403f, 0.93440f, 0.93478f, 0.93515f, + 0.93551f, 0.93588f, 0.93625f, 0.93661f, + 0.93698f, 0.93734f, 0.93770f, 0.93807f, + 0.93843f, 0.93878f, 0.93914f, 0.93950f, + 0.93986f, 0.94021f, 0.94056f, 0.94092f, + 0.94127f, 0.94162f, 0.94197f, 0.94231f, + 0.94266f, 0.94301f, 0.94335f, 0.94369f, + 0.94404f, 0.94438f, 0.94472f, 0.94506f, + 0.94540f, 0.94573f, 0.94607f, 0.94641f, + 0.94674f, 0.94707f, 0.94740f, 0.94774f, + 0.94807f, 0.94839f, 0.94872f, 0.94905f, + 0.94937f, 0.94970f, 0.95002f, 0.95035f, + 0.95067f, 0.95099f, 0.95131f, 0.95163f, + 0.95194f, 0.95226f, 0.95257f, 0.95289f, + 0.95320f, 0.95351f, 0.95383f, 0.95414f, + 0.95445f, 0.95475f, 0.95506f, 0.95537f, + 0.95567f, 0.95598f, 0.95628f, 0.95658f, + 0.95688f, 0.95718f, 0.95748f, 0.95778f, + 0.95808f, 0.95838f, 0.95867f, 0.95897f, + 0.95926f, 0.95955f, 0.95984f, 0.96013f, + 0.96042f, 0.96071f, 0.96100f, 0.96129f, + 0.96157f, 0.96186f, 0.96214f, 0.96242f, + 0.96271f, 0.96299f, 0.96327f, 0.96355f, + 0.96382f, 0.96410f, 0.96438f, 0.96465f, + 0.96493f, 0.96520f, 0.96547f, 0.96574f, + 0.96602f, 0.96629f, 0.96655f, 0.96682f, + 0.96709f, 0.96735f, 0.96762f, 0.96788f, + 0.96815f, 0.96841f, 0.96867f, 0.96893f, + 0.96919f, 0.96945f, 0.96971f, 0.96996f, + 0.97022f, 0.97047f, 0.97073f, 0.97098f, + 0.97123f, 0.97149f, 0.97174f, 0.97199f, + 0.97223f, 0.97248f, 0.97273f, 0.97297f, + 0.97322f, 0.97346f, 0.97371f, 0.97395f, + 0.97419f, 0.97443f, 0.97467f, 0.97491f, + 0.97515f, 0.97539f, 0.97562f, 0.97586f, + 0.97609f, 0.97633f, 0.97656f, 0.97679f, + 0.97702f, 0.97725f, 0.97748f, 0.97771f, + 0.97794f, 0.97817f, 0.97839f, 0.97862f, + 0.97884f, 0.97907f, 0.97929f, 0.97951f, + 0.97973f, 0.97995f, 0.98017f, 0.98039f, + 0.98061f, 0.98082f, 0.98104f, 0.98125f, + 0.98147f, 0.98168f, 0.98189f, 0.98211f, + 0.98232f, 0.98253f, 0.98274f, 0.98295f, + 0.98315f, 0.98336f, 0.98357f, 0.98377f, + 0.98398f, 0.98418f, 0.98438f, 0.98458f, + 0.98478f, 0.98498f, 0.98518f, 0.98538f, + 0.98558f, 0.98578f, 0.98597f, 0.98617f, + 0.98636f, 0.98656f, 0.98675f, 0.98694f, + 0.98714f, 0.98733f, 0.98752f, 0.98771f, + 0.98789f, 0.98808f, 0.98827f, 0.98845f, + 0.98864f, 0.98882f, 0.98901f, 0.98919f, + 0.98937f, 0.98955f, 0.98973f, 0.98991f, + 0.99009f, 0.99027f, 0.99045f, 0.99063f, + 0.99080f, 0.99098f, 0.99115f, 0.99133f, + 0.99150f, 0.99167f, 0.99184f, 0.99201f, + 0.99218f, 0.99235f, 0.99252f, 0.99269f, + 0.99285f, 0.99302f, 0.99319f, 0.99335f, + 0.99351f, 0.99368f, 0.99384f, 0.99400f, + 0.99416f, 0.99432f, 0.99448f, 0.99464f, + 0.99480f, 0.99495f, 0.99511f, 0.99527f, + 0.99542f, 0.99558f, 0.99573f, 0.99588f, + 0.99603f, 0.99619f, 0.99634f, 0.99649f, + 0.99664f, 0.99678f, 0.99693f, 0.99708f, + 0.99722f, 0.99737f, 0.99751f, 0.99766f, + 0.99780f, 0.99794f, 0.99809f, 0.99823f, + 0.99837f, 0.99851f, 0.99865f, 0.99879f, + 0.99892f, 0.99906f, 0.99920f, 0.99933f, + 0.99947f, 0.99960f, 0.99974f, 0.99987f, + 1.00000f + }; + + const uint32 kTableSize = sizeof (kTable ) / + sizeof (kTable [0]); + + real32 y = (real32) x * (real32) (kTableSize - 1); + + int32 index = Pin_int32 (0, (int32) y, kTableSize - 2); + + real32 fract = y - (real32) index; + + return kTable [index ] * (1.0f - fract) + + kTable [index + 1] * ( fract); + + } + +/*****************************************************************************/ + +real64 dng_tone_curve_acr3_default::EvaluateInverse (real64 x) const + { + + static const real32 kTable [] = + { + 0.00000f, 0.00121f, 0.00237f, 0.00362f, + 0.00496f, 0.00621f, 0.00738f, 0.00848f, + 0.00951f, 0.01048f, 0.01139f, 0.01227f, + 0.01312f, 0.01393f, 0.01471f, 0.01547f, + 0.01620f, 0.01692f, 0.01763f, 0.01831f, + 0.01899f, 0.01965f, 0.02030f, 0.02094f, + 0.02157f, 0.02218f, 0.02280f, 0.02340f, + 0.02399f, 0.02458f, 0.02517f, 0.02574f, + 0.02631f, 0.02688f, 0.02744f, 0.02800f, + 0.02855f, 0.02910f, 0.02965f, 0.03019f, + 0.03072f, 0.03126f, 0.03179f, 0.03232f, + 0.03285f, 0.03338f, 0.03390f, 0.03442f, + 0.03493f, 0.03545f, 0.03596f, 0.03647f, + 0.03698f, 0.03749f, 0.03799f, 0.03849f, + 0.03899f, 0.03949f, 0.03998f, 0.04048f, + 0.04097f, 0.04146f, 0.04195f, 0.04244f, + 0.04292f, 0.04341f, 0.04389f, 0.04437f, + 0.04485f, 0.04533f, 0.04580f, 0.04628f, + 0.04675f, 0.04722f, 0.04769f, 0.04816f, + 0.04863f, 0.04910f, 0.04956f, 0.05003f, + 0.05049f, 0.05095f, 0.05141f, 0.05187f, + 0.05233f, 0.05278f, 0.05324f, 0.05370f, + 0.05415f, 0.05460f, 0.05505f, 0.05551f, + 0.05595f, 0.05640f, 0.05685f, 0.05729f, + 0.05774f, 0.05818f, 0.05863f, 0.05907f, + 0.05951f, 0.05995f, 0.06039f, 0.06083f, + 0.06126f, 0.06170f, 0.06214f, 0.06257f, + 0.06301f, 0.06344f, 0.06388f, 0.06431f, + 0.06474f, 0.06517f, 0.06560f, 0.06602f, + 0.06645f, 0.06688f, 0.06731f, 0.06773f, + 0.06815f, 0.06858f, 0.06900f, 0.06943f, + 0.06985f, 0.07027f, 0.07069f, 0.07111f, + 0.07152f, 0.07194f, 0.07236f, 0.07278f, + 0.07319f, 0.07361f, 0.07402f, 0.07444f, + 0.07485f, 0.07526f, 0.07567f, 0.07608f, + 0.07650f, 0.07691f, 0.07732f, 0.07772f, + 0.07813f, 0.07854f, 0.07895f, 0.07935f, + 0.07976f, 0.08016f, 0.08057f, 0.08098f, + 0.08138f, 0.08178f, 0.08218f, 0.08259f, + 0.08299f, 0.08339f, 0.08379f, 0.08419f, + 0.08459f, 0.08499f, 0.08539f, 0.08578f, + 0.08618f, 0.08657f, 0.08697f, 0.08737f, + 0.08776f, 0.08816f, 0.08855f, 0.08894f, + 0.08934f, 0.08973f, 0.09012f, 0.09051f, + 0.09091f, 0.09130f, 0.09169f, 0.09208f, + 0.09247f, 0.09286f, 0.09324f, 0.09363f, + 0.09402f, 0.09440f, 0.09479f, 0.09518f, + 0.09556f, 0.09595f, 0.09633f, 0.09672f, + 0.09710f, 0.09749f, 0.09787f, 0.09825f, + 0.09863f, 0.09901f, 0.09939f, 0.09978f, + 0.10016f, 0.10054f, 0.10092f, 0.10130f, + 0.10167f, 0.10205f, 0.10243f, 0.10281f, + 0.10319f, 0.10356f, 0.10394f, 0.10432f, + 0.10469f, 0.10507f, 0.10544f, 0.10582f, + 0.10619f, 0.10657f, 0.10694f, 0.10731f, + 0.10768f, 0.10806f, 0.10843f, 0.10880f, + 0.10917f, 0.10954f, 0.10991f, 0.11029f, + 0.11066f, 0.11103f, 0.11141f, 0.11178f, + 0.11215f, 0.11253f, 0.11290f, 0.11328f, + 0.11365f, 0.11403f, 0.11440f, 0.11478f, + 0.11516f, 0.11553f, 0.11591f, 0.11629f, + 0.11666f, 0.11704f, 0.11742f, 0.11780f, + 0.11818f, 0.11856f, 0.11894f, 0.11932f, + 0.11970f, 0.12008f, 0.12046f, 0.12084f, + 0.12122f, 0.12161f, 0.12199f, 0.12237f, + 0.12276f, 0.12314f, 0.12352f, 0.12391f, + 0.12429f, 0.12468f, 0.12506f, 0.12545f, + 0.12583f, 0.12622f, 0.12661f, 0.12700f, + 0.12738f, 0.12777f, 0.12816f, 0.12855f, + 0.12894f, 0.12933f, 0.12972f, 0.13011f, + 0.13050f, 0.13089f, 0.13129f, 0.13168f, + 0.13207f, 0.13247f, 0.13286f, 0.13325f, + 0.13365f, 0.13404f, 0.13444f, 0.13483f, + 0.13523f, 0.13563f, 0.13603f, 0.13642f, + 0.13682f, 0.13722f, 0.13762f, 0.13802f, + 0.13842f, 0.13882f, 0.13922f, 0.13962f, + 0.14003f, 0.14043f, 0.14083f, 0.14124f, + 0.14164f, 0.14204f, 0.14245f, 0.14285f, + 0.14326f, 0.14366f, 0.14407f, 0.14448f, + 0.14489f, 0.14530f, 0.14570f, 0.14611f, + 0.14652f, 0.14693f, 0.14734f, 0.14776f, + 0.14817f, 0.14858f, 0.14900f, 0.14941f, + 0.14982f, 0.15024f, 0.15065f, 0.15107f, + 0.15148f, 0.15190f, 0.15232f, 0.15274f, + 0.15316f, 0.15357f, 0.15399f, 0.15441f, + 0.15483f, 0.15526f, 0.15568f, 0.15610f, + 0.15652f, 0.15695f, 0.15737f, 0.15779f, + 0.15822f, 0.15864f, 0.15907f, 0.15950f, + 0.15992f, 0.16035f, 0.16078f, 0.16121f, + 0.16164f, 0.16207f, 0.16250f, 0.16293f, + 0.16337f, 0.16380f, 0.16423f, 0.16467f, + 0.16511f, 0.16554f, 0.16598f, 0.16641f, + 0.16685f, 0.16729f, 0.16773f, 0.16816f, + 0.16860f, 0.16904f, 0.16949f, 0.16993f, + 0.17037f, 0.17081f, 0.17126f, 0.17170f, + 0.17215f, 0.17259f, 0.17304f, 0.17349f, + 0.17393f, 0.17438f, 0.17483f, 0.17528f, + 0.17573f, 0.17619f, 0.17664f, 0.17709f, + 0.17754f, 0.17799f, 0.17845f, 0.17890f, + 0.17936f, 0.17982f, 0.18028f, 0.18073f, + 0.18119f, 0.18165f, 0.18211f, 0.18257f, + 0.18303f, 0.18350f, 0.18396f, 0.18442f, + 0.18489f, 0.18535f, 0.18582f, 0.18629f, + 0.18676f, 0.18723f, 0.18770f, 0.18817f, + 0.18864f, 0.18911f, 0.18958f, 0.19005f, + 0.19053f, 0.19100f, 0.19147f, 0.19195f, + 0.19243f, 0.19291f, 0.19339f, 0.19387f, + 0.19435f, 0.19483f, 0.19531f, 0.19579f, + 0.19627f, 0.19676f, 0.19724f, 0.19773f, + 0.19821f, 0.19870f, 0.19919f, 0.19968f, + 0.20017f, 0.20066f, 0.20115f, 0.20164f, + 0.20214f, 0.20263f, 0.20313f, 0.20362f, + 0.20412f, 0.20462f, 0.20512f, 0.20561f, + 0.20611f, 0.20662f, 0.20712f, 0.20762f, + 0.20812f, 0.20863f, 0.20913f, 0.20964f, + 0.21015f, 0.21066f, 0.21117f, 0.21168f, + 0.21219f, 0.21270f, 0.21321f, 0.21373f, + 0.21424f, 0.21476f, 0.21527f, 0.21579f, + 0.21631f, 0.21683f, 0.21735f, 0.21787f, + 0.21839f, 0.21892f, 0.21944f, 0.21997f, + 0.22049f, 0.22102f, 0.22155f, 0.22208f, + 0.22261f, 0.22314f, 0.22367f, 0.22420f, + 0.22474f, 0.22527f, 0.22581f, 0.22634f, + 0.22688f, 0.22742f, 0.22796f, 0.22850f, + 0.22905f, 0.22959f, 0.23013f, 0.23068f, + 0.23123f, 0.23178f, 0.23232f, 0.23287f, + 0.23343f, 0.23398f, 0.23453f, 0.23508f, + 0.23564f, 0.23620f, 0.23675f, 0.23731f, + 0.23787f, 0.23843f, 0.23899f, 0.23956f, + 0.24012f, 0.24069f, 0.24125f, 0.24182f, + 0.24239f, 0.24296f, 0.24353f, 0.24410f, + 0.24468f, 0.24525f, 0.24582f, 0.24640f, + 0.24698f, 0.24756f, 0.24814f, 0.24872f, + 0.24931f, 0.24989f, 0.25048f, 0.25106f, + 0.25165f, 0.25224f, 0.25283f, 0.25342f, + 0.25401f, 0.25460f, 0.25520f, 0.25579f, + 0.25639f, 0.25699f, 0.25759f, 0.25820f, + 0.25880f, 0.25940f, 0.26001f, 0.26062f, + 0.26122f, 0.26183f, 0.26244f, 0.26306f, + 0.26367f, 0.26429f, 0.26490f, 0.26552f, + 0.26614f, 0.26676f, 0.26738f, 0.26800f, + 0.26863f, 0.26925f, 0.26988f, 0.27051f, + 0.27114f, 0.27177f, 0.27240f, 0.27303f, + 0.27367f, 0.27431f, 0.27495f, 0.27558f, + 0.27623f, 0.27687f, 0.27751f, 0.27816f, + 0.27881f, 0.27945f, 0.28011f, 0.28076f, + 0.28141f, 0.28207f, 0.28272f, 0.28338f, + 0.28404f, 0.28470f, 0.28536f, 0.28602f, + 0.28669f, 0.28736f, 0.28802f, 0.28869f, + 0.28937f, 0.29004f, 0.29071f, 0.29139f, + 0.29207f, 0.29274f, 0.29342f, 0.29410f, + 0.29479f, 0.29548f, 0.29616f, 0.29685f, + 0.29754f, 0.29823f, 0.29893f, 0.29962f, + 0.30032f, 0.30102f, 0.30172f, 0.30242f, + 0.30312f, 0.30383f, 0.30453f, 0.30524f, + 0.30595f, 0.30667f, 0.30738f, 0.30809f, + 0.30881f, 0.30953f, 0.31025f, 0.31097f, + 0.31170f, 0.31242f, 0.31315f, 0.31388f, + 0.31461f, 0.31534f, 0.31608f, 0.31682f, + 0.31755f, 0.31829f, 0.31904f, 0.31978f, + 0.32053f, 0.32127f, 0.32202f, 0.32277f, + 0.32353f, 0.32428f, 0.32504f, 0.32580f, + 0.32656f, 0.32732f, 0.32808f, 0.32885f, + 0.32962f, 0.33039f, 0.33116f, 0.33193f, + 0.33271f, 0.33349f, 0.33427f, 0.33505f, + 0.33583f, 0.33662f, 0.33741f, 0.33820f, + 0.33899f, 0.33978f, 0.34058f, 0.34138f, + 0.34218f, 0.34298f, 0.34378f, 0.34459f, + 0.34540f, 0.34621f, 0.34702f, 0.34783f, + 0.34865f, 0.34947f, 0.35029f, 0.35111f, + 0.35194f, 0.35277f, 0.35360f, 0.35443f, + 0.35526f, 0.35610f, 0.35694f, 0.35778f, + 0.35862f, 0.35946f, 0.36032f, 0.36117f, + 0.36202f, 0.36287f, 0.36372f, 0.36458f, + 0.36545f, 0.36631f, 0.36718f, 0.36805f, + 0.36891f, 0.36979f, 0.37066f, 0.37154f, + 0.37242f, 0.37331f, 0.37419f, 0.37507f, + 0.37596f, 0.37686f, 0.37775f, 0.37865f, + 0.37955f, 0.38045f, 0.38136f, 0.38227f, + 0.38317f, 0.38409f, 0.38500f, 0.38592f, + 0.38684f, 0.38776f, 0.38869f, 0.38961f, + 0.39055f, 0.39148f, 0.39242f, 0.39335f, + 0.39430f, 0.39524f, 0.39619f, 0.39714f, + 0.39809f, 0.39904f, 0.40000f, 0.40097f, + 0.40193f, 0.40289f, 0.40386f, 0.40483f, + 0.40581f, 0.40679f, 0.40777f, 0.40875f, + 0.40974f, 0.41073f, 0.41172f, 0.41272f, + 0.41372f, 0.41472f, 0.41572f, 0.41673f, + 0.41774f, 0.41875f, 0.41977f, 0.42079f, + 0.42181f, 0.42284f, 0.42386f, 0.42490f, + 0.42594f, 0.42697f, 0.42801f, 0.42906f, + 0.43011f, 0.43116f, 0.43222f, 0.43327f, + 0.43434f, 0.43540f, 0.43647f, 0.43754f, + 0.43862f, 0.43970f, 0.44077f, 0.44186f, + 0.44295f, 0.44404f, 0.44514f, 0.44624f, + 0.44734f, 0.44845f, 0.44956f, 0.45068f, + 0.45179f, 0.45291f, 0.45404f, 0.45516f, + 0.45630f, 0.45744f, 0.45858f, 0.45972f, + 0.46086f, 0.46202f, 0.46318f, 0.46433f, + 0.46550f, 0.46667f, 0.46784f, 0.46901f, + 0.47019f, 0.47137f, 0.47256f, 0.47375f, + 0.47495f, 0.47615f, 0.47735f, 0.47856f, + 0.47977f, 0.48099f, 0.48222f, 0.48344f, + 0.48467f, 0.48590f, 0.48714f, 0.48838f, + 0.48963f, 0.49088f, 0.49213f, 0.49340f, + 0.49466f, 0.49593f, 0.49721f, 0.49849f, + 0.49977f, 0.50106f, 0.50236f, 0.50366f, + 0.50496f, 0.50627f, 0.50758f, 0.50890f, + 0.51023f, 0.51155f, 0.51289f, 0.51422f, + 0.51556f, 0.51692f, 0.51827f, 0.51964f, + 0.52100f, 0.52237f, 0.52374f, 0.52512f, + 0.52651f, 0.52790f, 0.52930f, 0.53070f, + 0.53212f, 0.53353f, 0.53495f, 0.53638f, + 0.53781f, 0.53925f, 0.54070f, 0.54214f, + 0.54360f, 0.54506f, 0.54653f, 0.54800f, + 0.54949f, 0.55098f, 0.55247f, 0.55396f, + 0.55548f, 0.55699f, 0.55851f, 0.56003f, + 0.56156f, 0.56310f, 0.56464f, 0.56621f, + 0.56777f, 0.56933f, 0.57091f, 0.57248f, + 0.57407f, 0.57568f, 0.57727f, 0.57888f, + 0.58050f, 0.58213f, 0.58376f, 0.58541f, + 0.58705f, 0.58871f, 0.59037f, 0.59204f, + 0.59373f, 0.59541f, 0.59712f, 0.59882f, + 0.60052f, 0.60226f, 0.60399f, 0.60572f, + 0.60748f, 0.60922f, 0.61099f, 0.61276f, + 0.61455f, 0.61635f, 0.61814f, 0.61996f, + 0.62178f, 0.62361f, 0.62545f, 0.62730f, + 0.62917f, 0.63104f, 0.63291f, 0.63480f, + 0.63671f, 0.63862f, 0.64054f, 0.64249f, + 0.64443f, 0.64638f, 0.64835f, 0.65033f, + 0.65232f, 0.65433f, 0.65633f, 0.65836f, + 0.66041f, 0.66245f, 0.66452f, 0.66660f, + 0.66868f, 0.67078f, 0.67290f, 0.67503f, + 0.67717f, 0.67932f, 0.68151f, 0.68368f, + 0.68587f, 0.68809f, 0.69033f, 0.69257f, + 0.69482f, 0.69709f, 0.69939f, 0.70169f, + 0.70402f, 0.70634f, 0.70869f, 0.71107f, + 0.71346f, 0.71587f, 0.71829f, 0.72073f, + 0.72320f, 0.72567f, 0.72818f, 0.73069f, + 0.73323f, 0.73579f, 0.73838f, 0.74098f, + 0.74360f, 0.74622f, 0.74890f, 0.75159f, + 0.75429f, 0.75704f, 0.75979f, 0.76257f, + 0.76537f, 0.76821f, 0.77109f, 0.77396f, + 0.77688f, 0.77982f, 0.78278f, 0.78579f, + 0.78883f, 0.79187f, 0.79498f, 0.79809f, + 0.80127f, 0.80445f, 0.80767f, 0.81095f, + 0.81424f, 0.81757f, 0.82094f, 0.82438f, + 0.82782f, 0.83133f, 0.83488f, 0.83847f, + 0.84210f, 0.84577f, 0.84951f, 0.85328f, + 0.85713f, 0.86103f, 0.86499f, 0.86900f, + 0.87306f, 0.87720f, 0.88139f, 0.88566f, + 0.89000f, 0.89442f, 0.89891f, 0.90350f, + 0.90818f, 0.91295f, 0.91780f, 0.92272f, + 0.92780f, 0.93299f, 0.93828f, 0.94369f, + 0.94926f, 0.95493f, 0.96082f, 0.96684f, + 0.97305f, 0.97943f, 0.98605f, 0.99291f, + 1.00000f + }; + + const uint32 kTableSize = sizeof (kTable ) / + sizeof (kTable [0]); + + real32 y = (real32) x * (real32) (kTableSize - 1); + + int32 index = Pin_int32 (0, (int32) y, kTableSize - 2); + + real32 fract = y - (real32) index; + + return kTable [index ] * (1.0f - fract) + + kTable [index + 1] * ( fract); + + } + +/*****************************************************************************/ + +const dng_1d_function & dng_tone_curve_acr3_default::Get () + { + + static dng_tone_curve_acr3_default static_dng_tone_curve_acr3_default; + + return static_dng_tone_curve_acr3_default; + + } + +/*****************************************************************************/ + +class dng_render_task: public dng_filter_task + { + + protected: + + const dng_negative &fNegative; + + const dng_render &fParams; + + dng_point fSrcOffset; + + dng_vector fCameraWhite; + dng_matrix fCameraToRGB; + + AutoPtr fHueSatMap; + + dng_1d_table fExposureRamp; + + AutoPtr fLookTable; + + dng_1d_table fToneCurve; + + dng_matrix fRGBtoFinal; + + dng_1d_table fEncodeGamma; + + AutoPtr fHueSatMapEncode; + AutoPtr fHueSatMapDecode; + + AutoPtr fLookTableEncode; + AutoPtr fLookTableDecode; + + AutoPtr fTempBuffer [kMaxMPThreads]; + + public: + + dng_render_task (const dng_image &srcImage, + dng_image &dstImage, + const dng_negative &negative, + const dng_render ¶ms, + const dng_point &srcOffset); + + virtual dng_rect SrcArea (const dng_rect &dstArea); + + virtual void Start (uint32 threadCount, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer); + + virtual void ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer); + + }; + +/*****************************************************************************/ + +dng_render_task::dng_render_task (const dng_image &srcImage, + dng_image &dstImage, + const dng_negative &negative, + const dng_render ¶ms, + const dng_point &srcOffset) + + : dng_filter_task (srcImage, + dstImage) + + , fNegative (negative ) + , fParams (params ) + , fSrcOffset (srcOffset) + + , fCameraWhite () + , fCameraToRGB () + + , fHueSatMap () + + , fExposureRamp () + + , fLookTable () + + , fToneCurve () + + , fRGBtoFinal () + + , fEncodeGamma () + + , fHueSatMapEncode () + , fHueSatMapDecode () + + , fLookTableEncode () + , fLookTableDecode () + + { + + fSrcPixelType = ttFloat; + fDstPixelType = ttFloat; + + } + +/*****************************************************************************/ + +dng_rect dng_render_task::SrcArea (const dng_rect &dstArea) + { + + return dstArea + fSrcOffset; + + } + +/*****************************************************************************/ + +void dng_render_task::Start (uint32 threadCount, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer) + { + + dng_filter_task::Start (threadCount, + tileSize, + allocator, + sniffer); + + // Compute camera space to linear ProPhoto RGB parameters. + + dng_camera_profile_id profileID; // Default profile ID. + + if (!fNegative.IsMonochrome ()) + { + + AutoPtr spec (fNegative.MakeColorSpec (profileID)); + + if (fParams.WhiteXY ().IsValid ()) + { + + spec->SetWhiteXY (fParams.WhiteXY ()); + + } + + else if (fNegative.HasCameraNeutral ()) + { + + spec->SetWhiteXY (spec->NeutralToXY (fNegative.CameraNeutral ())); + + } + + else if (fNegative.HasCameraWhiteXY ()) + { + + spec->SetWhiteXY (fNegative.CameraWhiteXY ()); + + } + + else + { + + spec->SetWhiteXY (D55_xy_coord ()); + + } + + fCameraWhite = spec->CameraWhite (); + + fCameraToRGB = dng_space_ProPhoto::Get ().MatrixFromPCS () * + spec->CameraToPCS (); + + // Find Hue/Sat table, if any. + + const dng_camera_profile *profile = fNegative.ProfileByID (profileID); + + if (profile) + { + + fHueSatMap.Reset (profile->HueSatMapForWhite (spec->WhiteXY ())); + + if (profile->HasLookTable ()) + { + + fLookTable.Reset (new dng_hue_sat_map (profile->LookTable ())); + + } + + if (profile->HueSatMapEncoding () != encoding_Linear) + { + + BuildHueSatMapEncodingTable (*allocator, + profile->HueSatMapEncoding (), + fHueSatMapEncode, + fHueSatMapDecode, + false); + + } + + if (profile->LookTableEncoding () != encoding_Linear) + { + + BuildHueSatMapEncodingTable (*allocator, + profile->LookTableEncoding (), + fLookTableEncode, + fLookTableDecode, + false); + + } + + } + + } + + // Compute exposure/shadows ramp. + + real64 exposure = fParams.Exposure () + + fNegative.TotalBaselineExposure (profileID) - + (log (fNegative.Stage3Gain ()) / log (2.0)); + + { + + real64 white = 1.0 / pow (2.0, Max_real64 (0.0, exposure)); + + real64 black = fParams.Shadows () * + fNegative.ShadowScale () * + fNegative.Stage3Gain () * + 0.001; + + black = Min_real64 (black, 0.99 * white); + + dng_function_exposure_ramp rampFunction (white, + black, + black); + + fExposureRamp.Initialize (*allocator, rampFunction); + + } + + // Compute tone curve. + + { + + // If there is any negative exposure compenation to perform + // (beyond what the camera provides for with its baseline exposure), + // we fake this by darkening the tone curve. + + dng_function_exposure_tone exposureTone (exposure); + + dng_1d_concatenate totalTone (exposureTone, + fParams.ToneCurve ()); + + fToneCurve.Initialize (*allocator, totalTone); + + } + + // Compute linear ProPhoto RGB to final space parameters. + + { + + const dng_color_space &finalSpace = fParams.FinalSpace (); + + fRGBtoFinal = finalSpace.MatrixFromPCS () * + dng_space_ProPhoto::Get ().MatrixToPCS (); + + fEncodeGamma.Initialize (*allocator, finalSpace.GammaFunction ()); + + } + + // Allocate temp buffer to hold one row of RGB data. + + uint32 tempBufferSize = tileSize.h * (uint32) sizeof (real32) * 3; + + for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++) + { + + fTempBuffer [threadIndex] . Reset (allocator->Allocate (tempBufferSize)); + + } + + } + +/*****************************************************************************/ + +void dng_render_task::ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) + { + + dng_rect srcArea = srcBuffer.fArea; + dng_rect dstArea = dstBuffer.fArea; + + uint32 srcCols = srcArea.W (); + + real32 *tPtrR = fTempBuffer [threadIndex]->Buffer_real32 (); + + real32 *tPtrG = tPtrR + srcCols; + real32 *tPtrB = tPtrG + srcCols; + + for (int32 srcRow = srcArea.t; srcRow < srcArea.b; srcRow++) + { + + // First convert from camera native space to linear PhotoRGB, + // applying the white balance and camera profile. + + { + + const real32 *sPtrA = (const real32 *) + srcBuffer.ConstPixel (srcRow, + srcArea.l, + 0); + + if (fSrcPlanes == 1) + { + + // For monochrome cameras, this just requires copying + // the data into all three color channels. + + DoCopyBytes (sPtrA, tPtrR, srcCols * (uint32) sizeof (real32)); + DoCopyBytes (sPtrA, tPtrG, srcCols * (uint32) sizeof (real32)); + DoCopyBytes (sPtrA, tPtrB, srcCols * (uint32) sizeof (real32)); + + } + + else + { + + const real32 *sPtrB = sPtrA + srcBuffer.fPlaneStep; + const real32 *sPtrC = sPtrB + srcBuffer.fPlaneStep; + + if (fSrcPlanes == 3) + { + + DoBaselineABCtoRGB (sPtrA, + sPtrB, + sPtrC, + tPtrR, + tPtrG, + tPtrB, + srcCols, + fCameraWhite, + fCameraToRGB); + + } + + else + { + + const real32 *sPtrD = sPtrC + srcBuffer.fPlaneStep; + + DoBaselineABCDtoRGB (sPtrA, + sPtrB, + sPtrC, + sPtrD, + tPtrR, + tPtrG, + tPtrB, + srcCols, + fCameraWhite, + fCameraToRGB); + + } + + // Apply Hue/Sat map, if any. + + if (fHueSatMap.Get ()) + { + + DoBaselineHueSatMap (tPtrR, + tPtrG, + tPtrB, + tPtrR, + tPtrG, + tPtrB, + srcCols, + *fHueSatMap.Get (), + fHueSatMapEncode.Get (), + fHueSatMapDecode.Get ()); + + } + + } + + } + + // Apply exposure curve. + + DoBaseline1DTable (tPtrR, + tPtrR, + srcCols, + fExposureRamp); + + DoBaseline1DTable (tPtrG, + tPtrG, + srcCols, + fExposureRamp); + + DoBaseline1DTable (tPtrB, + tPtrB, + srcCols, + fExposureRamp); + + // Apply look table, if any. + + if (fLookTable.Get ()) + { + + DoBaselineHueSatMap (tPtrR, + tPtrG, + tPtrB, + tPtrR, + tPtrG, + tPtrB, + srcCols, + *fLookTable.Get (), + fLookTableEncode.Get (), + fLookTableDecode.Get ()); + + } + + // Apply baseline tone curve. + + DoBaselineRGBTone (tPtrR, + tPtrG, + tPtrB, + tPtrR, + tPtrG, + tPtrB, + srcCols, + fToneCurve); + + // Convert to final color space. + + int32 dstRow = srcRow + (dstArea.t - srcArea.t); + + if (fDstPlanes == 1) + { + + real32 *dPtrG = dstBuffer.DirtyPixel_real32 (dstRow, + dstArea.l, + 0); + + DoBaselineRGBtoGray (tPtrR, + tPtrG, + tPtrB, + dPtrG, + srcCols, + fRGBtoFinal); + + DoBaseline1DTable (dPtrG, + dPtrG, + srcCols, + fEncodeGamma); + + } + + else + { + + real32 *dPtrR = dstBuffer.DirtyPixel_real32 (dstRow, + dstArea.l, + 0); + + real32 *dPtrG = dPtrR + dstBuffer.fPlaneStep; + real32 *dPtrB = dPtrG + dstBuffer.fPlaneStep; + + DoBaselineRGBtoRGB (tPtrR, + tPtrG, + tPtrB, + dPtrR, + dPtrG, + dPtrB, + srcCols, + fRGBtoFinal); + + DoBaseline1DTable (dPtrR, + dPtrR, + srcCols, + fEncodeGamma); + + DoBaseline1DTable (dPtrG, + dPtrG, + srcCols, + fEncodeGamma); + + DoBaseline1DTable (dPtrB, + dPtrB, + srcCols, + fEncodeGamma); + + } + + } + + } + +/*****************************************************************************/ + +dng_render::dng_render (dng_host &host, + const dng_negative &negative) + + : fHost (host) + , fNegative (negative) + + , fWhiteXY () + + , fExposure (0.0) + , fShadows (5.0) + + , fToneCurve (&dng_tone_curve_acr3_default::Get ()) + + , fFinalSpace (&dng_space_sRGB::Get ()) + , fFinalPixelType (ttByte) + + , fMaximumSize (0) + + , fProfileToneCurve () + + { + + // Switch to NOP default parameters for non-scene referred data. + + if (fNegative.ColorimetricReference () != crSceneReferred) + { + + fShadows = 0.0; + + fToneCurve = &dng_1d_identity::Get (); + + } + + // Use default tone curve from profile if any. + + const dng_camera_profile *profile = fNegative.ProfileByID (dng_camera_profile_id ()); + + if (profile && profile->ToneCurve ().IsValid ()) + { + + fProfileToneCurve.Reset (new dng_spline_solver); + + profile->ToneCurve ().Solve (*fProfileToneCurve.Get ()); + + fToneCurve = fProfileToneCurve.Get (); + + } + + // Turn off default shadow mapping if requested by profile. + + if (profile && (profile->DefaultBlackRender () == defaultBlackRender_None)) + { + + fShadows = 0.0; + + } + + } + +/*****************************************************************************/ + +dng_image * dng_render::Render () + { + + const dng_image *srcImage = fNegative.Stage3Image (); + + dng_rect srcBounds = fNegative.DefaultCropArea (); + + dng_point dstSize; + + dstSize.h = fNegative.DefaultFinalWidth (); + dstSize.v = fNegative.DefaultFinalHeight (); + + if (MaximumSize ()) + { + + if (Max_uint32 (dstSize.h, dstSize.v) > MaximumSize ()) + { + + real64 ratio = fNegative.AspectRatio (); + + if (ratio >= 1.0) + { + dstSize.h = MaximumSize (); + dstSize.v = Max_uint32 (1, Round_uint32 (dstSize.h / ratio)); + } + + else + { + dstSize.v = MaximumSize (); + dstSize.h = Max_uint32 (1, Round_uint32 (dstSize.v * ratio)); + } + + } + + } + + AutoPtr tempImage; + + if (srcBounds.Size () != dstSize) + { + + tempImage.Reset (fHost.Make_dng_image (dstSize, + srcImage->Planes (), + srcImage->PixelType ())); + + ResampleImage (fHost, + *srcImage, + *tempImage.Get (), + srcBounds, + tempImage->Bounds (), + dng_resample_bicubic::Get ()); + + srcImage = tempImage.Get (); + + srcBounds = tempImage->Bounds (); + + } + + uint32 dstPlanes = FinalSpace ().IsMonochrome () ? 1 : 3; + + AutoPtr dstImage (fHost.Make_dng_image (srcBounds.Size (), + dstPlanes, + FinalPixelType ())); + + dng_render_task task (*srcImage, + *dstImage.Get (), + fNegative, + *this, + srcBounds.TL ()); + + fHost.PerformAreaTask (task, + dstImage->Bounds ()); + + return dstImage.Release (); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_render.h b/source/lib/dng_sdk/dng_render.h new file mode 100644 index 0000000..4c9aec7 --- /dev/null +++ b/source/lib/dng_sdk/dng_render.h @@ -0,0 +1,312 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_render.h#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * Classes for conversion of RAW data to final image. + */ + +/*****************************************************************************/ + +#ifndef __dng_render__ +#define __dng_render__ + +/*****************************************************************************/ + +#include "dng_1d_function.h" +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_spline.h" +#include "dng_xy_coord.h" + +/******************************************************************************/ + +/// \brief Curve for pre-exposure-compensation adjustment based on noise floor, +/// shadows, and highlight level. + +class dng_function_exposure_ramp: public dng_1d_function + { + + public: + + real64 fSlope; // Slope of straight segment. + + real64 fBlack; // Intercept of straight segment. + + real64 fRadius; // Rounding radius. + + real64 fQScale; // Quadradic scale. + + public: + + dng_function_exposure_ramp (real64 white, + real64 black, + real64 minBlack); + + virtual real64 Evaluate (real64 x) const; + + }; + +/******************************************************************************/ + +/// \brief Exposure compensation curve for a given compensation amount in stops using +/// quadric for roll-off. + +class dng_function_exposure_tone: public dng_1d_function + { + + protected: + + bool fIsNOP; // Is this a NOP function? + + real64 fSlope; // Slope for lower part of curve. + + real64 a; // Quadradic parameters for upper two f-stops. + real64 b; + real64 c; + + public: + + dng_function_exposure_tone (real64 exposure); + + /// Returns output value for a given input tone. + + virtual real64 Evaluate (real64 x) const; + + }; + +/*****************************************************************************/ + +/// Default ACR3 tone curve. + +class dng_tone_curve_acr3_default: public dng_1d_function + { + + public: + + /// Returns output value for a given input tone. + + virtual real64 Evaluate (real64 x) const; + + /// Returns nearest input value for a given output tone. + + virtual real64 EvaluateInverse (real64 x) const; + + static const dng_1d_function & Get (); + + }; + +/*****************************************************************************/ + +/// \brief Encoding gamma curve for a given color space. + +class dng_function_gamma_encode: public dng_1d_function + { + + protected: + + const dng_color_space &fSpace; + + public: + + dng_function_gamma_encode (const dng_color_space &space); + + virtual real64 Evaluate (real64 x) const; + + }; + +/*****************************************************************************/ + +/// \brief Class used to render digital negative to displayable image. + +class dng_render + { + + protected: + + dng_host &fHost; + + const dng_negative &fNegative; + + dng_xy_coord fWhiteXY; + + real64 fExposure; + + real64 fShadows; + + const dng_1d_function *fToneCurve; + + const dng_color_space *fFinalSpace; + + uint32 fFinalPixelType; + + uint32 fMaximumSize; + + private: + + AutoPtr fProfileToneCurve; + + public: + + /// Construct a rendering instance that will be used to convert a given digital negative. + /// \param host The host to use for memory allocation, progress updates, and abort testing. + /// \param negative The digital negative to convert to a displayable image. + + dng_render (dng_host &host, + const dng_negative &negative); + + virtual ~dng_render () + { + } + + /// Set the white point to be used for conversion. + /// \param white White point to use. + + void SetWhiteXY (const dng_xy_coord &white) + { + fWhiteXY = white; + } + + /// Get the white point to be used for conversion. + /// \retval White point to use. + + const dng_xy_coord WhiteXY () const + { + return fWhiteXY; + } + + /// Set exposure compensation. + /// \param exposure Compensation value in stops, positive or negative. + + void SetExposure (real64 exposure) + { + fExposure = exposure; + } + + /// Get exposure compensation. + /// \retval Compensation value in stops, positive or negative. + + real64 Exposure () const + { + return fExposure; + } + + /// Set shadow clip amount. + /// \param shadows Shadow clip amount. + + void SetShadows (real64 shadows) + { + fShadows = shadows; + } + + /// Get shadow clip amount. + /// \retval Shadow clip amount. + + real64 Shadows () const + { + return fShadows; + } + + /// Set custom tone curve for conversion. + /// \param curve 1D function that defines tone mapping to use during conversion. + + void SetToneCurve (const dng_1d_function &curve) + { + fToneCurve = &curve; + } + + /// Get custom tone curve for conversion. + /// \retval 1D function that defines tone mapping to use during conversion. + + const dng_1d_function & ToneCurve () const + { + return *fToneCurve; + } + + /// Set final color space in which resulting image data should be represented. + /// (See dng_color_space.h for possible values.) + /// \param space Color space to use. + + void SetFinalSpace (const dng_color_space &space) + { + fFinalSpace = &space; + } + + /// Get final color space in which resulting image data should be represented. + /// \retval Color space to use. + + const dng_color_space & FinalSpace () const + { + return *fFinalSpace; + } + + /// Set pixel type of final image data. + /// Can be ttByte (default), ttShort, or ttFloat. + /// \param type Pixel type to use. + + void SetFinalPixelType (uint32 type) + { + fFinalPixelType = type; + } + + /// Get pixel type of final image data. + /// Can be ttByte (default), ttShort, or ttFloat. + /// \retval Pixel type to use. + + uint32 FinalPixelType () const + { + return fFinalPixelType; + } + + /// Set maximum dimension, in pixels, of resulting image. + /// If final image would have either dimension larger than maximum, the larger + /// of the two dimensions is set to this maximum size and the smaller dimension + /// is adjusted to preserve aspect ratio. + /// \param size Maximum size to allow. + + void SetMaximumSize (uint32 size) + { + fMaximumSize = size; + } + + /// Get maximum dimension, in pixels, of resulting image. + /// If the final image would have either dimension larger than this maximum, the larger + /// of the two dimensions is set to this maximum size and the smaller dimension + /// is adjusted to preserve the image's aspect ratio. + /// \retval Maximum allowed size. + + uint32 MaximumSize () const + { + return fMaximumSize; + } + + /// Actually render a digital negative to a displayable image. + /// Input digital negative is passed to the constructor of this dng_render class. + /// \retval The final resulting image. + + virtual dng_image * Render (); + + private: + + // Hidden copy constructor and assignment operator. + + dng_render (const dng_render &render); + + dng_render & operator= (const dng_render &render); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_resample.cpp b/source/lib/dng_sdk/dng_resample.cpp new file mode 100644 index 0000000..5a06465 --- /dev/null +++ b/source/lib/dng_sdk/dng_resample.cpp @@ -0,0 +1,781 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_resample.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_resample.h" + +#include "dng_assertions.h" +#include "dng_bottlenecks.h" +#include "dng_filter_task.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_memory.h" +#include "dng_pixel_buffer.h" +#include "dng_tag_types.h" +#include "dng_utils.h" + +/******************************************************************************/ + +real64 dng_resample_bicubic::Extent () const + { + + return 2.0; + + } + +/******************************************************************************/ + +real64 dng_resample_bicubic::Evaluate (real64 x) const + { + + const real64 A = -0.75; + + x = Abs_real64 (x); + + if (x >= 2.0) + return 0.0; + + else if (x >= 1.0) + return (((A * x - 5.0 * A) * x + 8.0 * A) * x - 4.0 * A); + + else + return (((A + 2.0) * x - (A + 3.0)) * x * x + 1.0); + + } + +/******************************************************************************/ + +const dng_resample_function & dng_resample_bicubic::Get () + { + + static dng_resample_bicubic static_dng_resample_bicubic; + + return static_dng_resample_bicubic; + + } + +/*****************************************************************************/ + +dng_resample_coords::dng_resample_coords () + + : fOrigin (0) + , fCoords () + + { + + } + +/*****************************************************************************/ + +dng_resample_coords::~dng_resample_coords () + { + + } + +/*****************************************************************************/ + +void dng_resample_coords::Initialize (int32 srcOrigin, + int32 dstOrigin, + uint32 srcCount, + uint32 dstCount, + dng_memory_allocator &allocator) + { + + fOrigin = dstOrigin; + + uint32 dstEntries = RoundUp8 (dstCount); + + fCoords.Reset (allocator.Allocate (dstEntries * (uint32) sizeof (int32))); + + int32 *coords = fCoords->Buffer_int32 (); + + real64 invScale = (real64) srcCount / + (real64) dstCount; + + for (uint32 j = 0; j < dstCount; j++) + { + + real64 x = (real64) j + 0.5; + + real64 y = x * invScale - 0.5 + (real64) srcOrigin; + + coords [j] = Round_int32 (y * (real64) kResampleSubsampleCount); + + } + + // Pad out table by replicating last entry. + + for (uint32 k = dstCount; k < dstEntries; k++) + { + + coords [k] = coords [dstCount - 1]; + + } + + } + +/*****************************************************************************/ + +dng_resample_weights::dng_resample_weights () + + : fRadius (0) + + , fWeightStep (0) + + , fWeights32 () + , fWeights16 () + + { + + } + +/*****************************************************************************/ + +dng_resample_weights::~dng_resample_weights () + { + + } + +/*****************************************************************************/ + +void dng_resample_weights::Initialize (real64 scale, + const dng_resample_function &kernel, + dng_memory_allocator &allocator) + { + + uint32 j; + + // We only adjust the kernel size for scale factors less than 1.0. + + scale = Min_real64 (scale, 1.0); + + // Find radius of this kernel. + + fRadius = (uint32) (kernel.Extent () / scale + 0.9999); + + // Width is twice the radius. + + uint32 width = fRadius * 2; + + // Round to each set to weights to a multiple of 8 entries. + + fWeightStep = RoundUp8 (width); + + // Allocate and zero weight tables. + + fWeights32.Reset (allocator.Allocate (fWeightStep * + kResampleSubsampleCount * + (uint32) sizeof (real32))); + + DoZeroBytes (fWeights32->Buffer (), + fWeights32->LogicalSize ()); + + fWeights16.Reset (allocator.Allocate (fWeightStep * + kResampleSubsampleCount * + (uint32) sizeof (int16))); + + DoZeroBytes (fWeights16->Buffer (), + fWeights16->LogicalSize ()); + + // Compute kernel for each subsample values. + + for (uint32 sample = 0; sample < kResampleSubsampleCount; sample++) + { + + real64 fract = sample * (1.0 / (real64) kResampleSubsampleCount); + + real32 *w32 = fWeights32->Buffer_real32 () + fWeightStep * sample; + + // Evaluate kernel function for 32 bit weights. + + { + + real64 t32 = 0.0; + + for (j = 0; j < width; j++) + { + + int32 k = j - fRadius + 1; + + real64 x = (k - fract) * scale; + + w32 [j] = (real32) kernel.Evaluate (x); + + t32 += w32 [j]; + + } + + // Scale 32 bit weights so total of weights is 1.0. + + real32 s32 = (real32) (1.0 / t32); + + for (j = 0; j < width; j++) + { + + w32 [j] *= s32; + + } + + } + + // Round off 32 bit weights to 16 bit weights. + + { + + int16 *w16 = fWeights16->Buffer_int16 () + fWeightStep * sample; + + int32 t16 = 0; + + for (j = 0; j < width; j++) + { + + w16 [j] = (int16) Round_int32 (w32 [j] * 16384.0); + + t16 += w16 [j]; + + } + + // Adjust center entry for any round off error so total is + // exactly 16384. + + w16 [fRadius - (fract >= 0.5 ? 0 : 1)] += (int16) (16384 - t16); + + } + + } + + } + +/*****************************************************************************/ + +dng_resample_weights_2d::dng_resample_weights_2d () + + : fRadius (0) + + , fRowStep (0) + , fColStep (0) + + , fWeights32 () + , fWeights16 () + + { + + } + +/*****************************************************************************/ + +dng_resample_weights_2d::~dng_resample_weights_2d () + { + + } + +/*****************************************************************************/ + +void dng_resample_weights_2d::Initialize (const dng_resample_function &kernel, + dng_memory_allocator &allocator) + { + + // Find radius of this kernel. Unlike with 1d resample weights (see + // dng_resample_weights), we never scale up the kernel size. + + fRadius = (uint32) (kernel.Extent () + 0.9999); + + // Width is twice the radius. + + const uint32 width = fRadius * 2; + const uint32 widthSqr = width * width; + + const uint32 step = RoundUp8 (width * width); + + fRowStep = step * kResampleSubsampleCount2D; + fColStep = step; + + // Allocate and zero weight tables. + + fWeights32.Reset (allocator.Allocate (step * + kResampleSubsampleCount2D * + kResampleSubsampleCount2D * + (uint32) sizeof (real32))); + + DoZeroBytes (fWeights32->Buffer (), + fWeights32->LogicalSize ()); + + fWeights16.Reset (allocator.Allocate (step * + kResampleSubsampleCount2D * + kResampleSubsampleCount2D * + (uint32) sizeof (int16))); + + DoZeroBytes (fWeights16->Buffer (), + fWeights16->LogicalSize ()); + + // Compute kernel for each subsample values. + + for (uint32 y = 0; y < kResampleSubsampleCount2D; y++) + { + + real64 yFract = y * (1.0 / (real64) kResampleSubsampleCount2D); + + for (uint32 x = 0; x < kResampleSubsampleCount2D; x++) + { + + real64 xFract = x * (1.0 / (real64) kResampleSubsampleCount2D); + + real32 *w32 = (real32 *) Weights32 (dng_point ((int32) y, + (int32) x)); + + // Evaluate kernel function for 32 bit weights. + + { + + real64 t32 = 0.0; + + uint32 index = 0; + + for (uint32 i = 0; i < width; i++) + { + + int32 yInt = ((int32) i) - fRadius + 1; + real64 yPos = yInt - yFract; + + for (uint32 j = 0; j < width; j++) + { + + int32 xInt = ((int32) j) - fRadius + 1; + real64 xPos = xInt - xFract; + + #if 0 + + // Radial. + + real64 dy2 = yPos * yPos; + real64 dx2 = xPos * xPos; + + real64 r = sqrt (dx2 + dy2); + + w32 [index] = (real32) kernel.Evaluate (r); + + #else + + // Separable. + + w32 [index] = (real32) kernel.Evaluate (xPos) * + (real32) kernel.Evaluate (yPos); + + #endif + + t32 += w32 [index]; + + index++; + + } + + } + + // Scale 32 bit weights so total of weights is 1.0. + + const real32 s32 = (real32) (1.0 / t32); + + for (uint32 i = 0; i < widthSqr; i++) + { + + w32 [i] *= s32; + + } + + } + + // Round off 32 bit weights to 16 bit weights. + + { + + int16 *w16 = (int16 *) Weights16 (dng_point ((int32) y, + (int32) x)); + + int32 t16 = 0; + + for (uint32 j = 0; j < widthSqr; j++) + { + + w16 [j] = (int16) Round_int32 (w32 [j] * 16384.0); + + t16 += w16 [j]; + + } + + // Adjust one of the center entries for any round off error so total + // is exactly 16384. + + const uint32 xOffset = fRadius - ((xFract >= 0.5) ? 0 : 1); + const uint32 yOffset = fRadius - ((yFract >= 0.5) ? 0 : 1); + const uint32 centerOffset = width * yOffset + xOffset; + + w16 [centerOffset] += (int16) (16384 - t16); + + } + + } + + } + + } + +/*****************************************************************************/ + +class dng_resample_task: public dng_filter_task + { + + protected: + + dng_rect fSrcBounds; + dng_rect fDstBounds; + + const dng_resample_function &fKernel; + + real64 fRowScale; + real64 fColScale; + + dng_resample_coords fRowCoords; + dng_resample_coords fColCoords; + + dng_resample_weights fWeightsV; + dng_resample_weights fWeightsH; + + dng_point fSrcTileSize; + + AutoPtr fTempBuffer [kMaxMPThreads]; + + public: + + dng_resample_task (const dng_image &srcImage, + dng_image &dstImage, + const dng_rect &srcBounds, + const dng_rect &dstBounds, + const dng_resample_function &kernel); + + virtual dng_rect SrcArea (const dng_rect &dstArea); + + virtual dng_point SrcTileSize (const dng_point &dstTileSize); + + virtual void Start (uint32 threadCount, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer); + + virtual void ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer); + + }; + +/*****************************************************************************/ + +dng_resample_task::dng_resample_task (const dng_image &srcImage, + dng_image &dstImage, + const dng_rect &srcBounds, + const dng_rect &dstBounds, + const dng_resample_function &kernel) + + : dng_filter_task (srcImage, + dstImage) + + , fSrcBounds (srcBounds) + , fDstBounds (dstBounds) + + , fKernel (kernel) + + , fRowScale (dstBounds.H () / (real64) srcBounds.H ()) + , fColScale (dstBounds.W () / (real64) srcBounds.W ()) + + , fRowCoords () + , fColCoords () + + , fWeightsV () + , fWeightsH () + + , fSrcTileSize () + + { + + if (srcImage.PixelSize () <= 2 && + dstImage.PixelSize () <= 2 && + srcImage.PixelRange () == dstImage.PixelRange ()) + { + fSrcPixelType = ttShort; + fDstPixelType = ttShort; + } + + else + { + fSrcPixelType = ttFloat; + fDstPixelType = ttFloat; + } + + fUnitCell = dng_point (8, 8); + + fMaxTileSize.v = Pin_int32 (fUnitCell.v, + Round_int32 (fMaxTileSize.v * fRowScale), + fMaxTileSize.v); + + fMaxTileSize.h = Pin_int32 (fUnitCell.h, + Round_int32 (fMaxTileSize.h * fColScale), + fMaxTileSize.h); + + } + +/*****************************************************************************/ + +dng_rect dng_resample_task::SrcArea (const dng_rect &dstArea) + { + + int32 offsetV = fWeightsV.Offset (); + int32 offsetH = fWeightsH.Offset (); + + uint32 widthV = fWeightsV.Width (); + uint32 widthH = fWeightsH.Width (); + + dng_rect srcArea; + + srcArea.t = fRowCoords.Pixel (dstArea.t) + offsetV; + srcArea.l = fColCoords.Pixel (dstArea.l) + offsetH; + + srcArea.b = fRowCoords.Pixel (dstArea.b - 1) + offsetV + widthV; + srcArea.r = fColCoords.Pixel (dstArea.r - 1) + offsetH + widthH; + + return srcArea; + + } + +/*****************************************************************************/ + +dng_point dng_resample_task::SrcTileSize (const dng_point & /* dstTileSize */) + { + + return fSrcTileSize; + + } + +/*****************************************************************************/ + +void dng_resample_task::Start (uint32 threadCount, + const dng_point &tileSize, + dng_memory_allocator *allocator, + dng_abort_sniffer *sniffer) + { + + // Compute sub-pixel resolution coordinates in the source image for + // each row and column of the destination area. + + fRowCoords.Initialize (fSrcBounds.t, + fDstBounds.t, + fSrcBounds.H (), + fDstBounds.H (), + *allocator); + + fColCoords.Initialize (fSrcBounds.l, + fDstBounds.l, + fSrcBounds.W (), + fDstBounds.W (), + *allocator); + + // Compute resampling kernels. + + fWeightsV.Initialize (fRowScale, + fKernel, + *allocator); + + fWeightsH.Initialize (fColScale, + fKernel, + *allocator); + + // Find upper bound on source source tile. + + fSrcTileSize.v = Round_int32 (tileSize.v / fRowScale) + fWeightsV.Width () + 2; + fSrcTileSize.h = Round_int32 (tileSize.h / fColScale) + fWeightsH.Width () + 2; + + // Allocate temp buffers. + + uint32 tempBufferSize = RoundUp8 (fSrcTileSize.h) * (uint32) sizeof (real32); + + for (uint32 threadIndex = 0; threadIndex < threadCount; threadIndex++) + { + + fTempBuffer [threadIndex] . Reset (allocator->Allocate (tempBufferSize)); + + } + + // Allocate the pixel buffers. + + dng_filter_task::Start (threadCount, + tileSize, + allocator, + sniffer); + + } + +/*****************************************************************************/ + +void dng_resample_task::ProcessArea (uint32 threadIndex, + dng_pixel_buffer &srcBuffer, + dng_pixel_buffer &dstBuffer) + { + + dng_rect srcArea = srcBuffer.fArea; + dng_rect dstArea = dstBuffer.fArea; + + uint32 srcCols = srcArea.W (); + uint32 dstCols = dstArea.W (); + + uint32 widthV = fWeightsV.Width (); + uint32 widthH = fWeightsH.Width (); + + int32 offsetV = fWeightsV.Offset (); + int32 offsetH = fWeightsH.Offset (); + + uint32 stepH = fWeightsH.Step (); + + const int32 *rowCoords = fRowCoords.Coords (0 ); + const int32 *colCoords = fColCoords.Coords (dstArea.l); + + if (fSrcPixelType == ttFloat) + { + + const real32 *weightsH = fWeightsH.Weights32 (0); + + real32 *tPtr = fTempBuffer [threadIndex]->Buffer_real32 (); + + real32 *ttPtr = tPtr + offsetH - srcArea.l; + + for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++) + { + + int32 rowCoord = rowCoords [dstRow]; + + int32 rowFract = rowCoord & kResampleSubsampleMask; + + const real32 *weightsV = fWeightsV.Weights32 (rowFract); + + int32 srcRow = (rowCoord >> kResampleSubsampleBits) + offsetV; + + for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++) + { + + const real32 *sPtr = srcBuffer.ConstPixel_real32 (srcRow, + srcArea.l, + plane); + + DoResampleDown32 (sPtr, + tPtr, + srcCols, + srcBuffer.fRowStep, + weightsV, + widthV); + + real32 *dPtr = dstBuffer.DirtyPixel_real32 (dstRow, + dstArea.l, + plane); + + DoResampleAcross32 (ttPtr, + dPtr, + dstCols, + colCoords, + weightsH, + widthH, + stepH); + + } + + } + + } + + else + { + + const int16 *weightsH = fWeightsH.Weights16 (0); + + uint16 *tPtr = fTempBuffer [threadIndex]->Buffer_uint16 (); + + uint16 *ttPtr = tPtr + offsetH - srcArea.l; + + uint32 pixelRange = fDstImage.PixelRange (); + + for (int32 dstRow = dstArea.t; dstRow < dstArea.b; dstRow++) + { + + int32 rowCoord = rowCoords [dstRow]; + + int32 rowFract = rowCoord & kResampleSubsampleMask; + + const int16 *weightsV = fWeightsV.Weights16 (rowFract); + + int32 srcRow = (rowCoord >> kResampleSubsampleBits) + offsetV; + + for (uint32 plane = 0; plane < dstBuffer.fPlanes; plane++) + { + + const uint16 *sPtr = srcBuffer.ConstPixel_uint16 (srcRow, + srcArea.l, + plane); + + DoResampleDown16 (sPtr, + tPtr, + srcCols, + srcBuffer.fRowStep, + weightsV, + widthV, + pixelRange); + + uint16 *dPtr = dstBuffer.DirtyPixel_uint16 (dstRow, + dstArea.l, + plane); + + DoResampleAcross16 (ttPtr, + dPtr, + dstCols, + colCoords, + weightsH, + widthH, + stepH, + pixelRange); + + } + + } + + } + + } + +/*****************************************************************************/ + +void ResampleImage (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + const dng_rect &srcBounds, + const dng_rect &dstBounds, + const dng_resample_function &kernel) + { + + dng_resample_task task (srcImage, + dstImage, + srcBounds, + dstBounds, + kernel); + + host.PerformAreaTask (task, + dstBounds); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_resample.h b/source/lib/dng_sdk/dng_resample.h new file mode 100644 index 0000000..07f42ef --- /dev/null +++ b/source/lib/dng_sdk/dng_resample.h @@ -0,0 +1,261 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_resample.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_resample__ +#define __dng_resample__ + +/*****************************************************************************/ + +#include "dng_assertions.h" +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_memory.h" +#include "dng_point.h" +#include "dng_types.h" + +/*****************************************************************************/ + +class dng_resample_function + { + + public: + + dng_resample_function () + { + } + + virtual ~dng_resample_function () + { + } + + virtual real64 Extent () const = 0; + + virtual real64 Evaluate (real64 x) const = 0; + + }; + +/*****************************************************************************/ + +class dng_resample_bicubic: public dng_resample_function + { + + public: + + virtual real64 Extent () const; + + virtual real64 Evaluate (real64 x) const; + + static const dng_resample_function & Get (); + + }; + +/******************************************************************************/ + +const uint32 kResampleSubsampleBits = 7; +const uint32 kResampleSubsampleCount = 1 << kResampleSubsampleBits; +const uint32 kResampleSubsampleMask = kResampleSubsampleCount - 1; + +/*****************************************************************************/ + +class dng_resample_coords + { + + protected: + + int32 fOrigin; + + AutoPtr fCoords; + + public: + + dng_resample_coords (); + + virtual ~dng_resample_coords (); + + void Initialize (int32 srcOrigin, + int32 dstOrigin, + uint32 srcCount, + uint32 dstCount, + dng_memory_allocator &allocator); + + const int32 * Coords (int32 index) const + { + return fCoords->Buffer_int32 () + (index - fOrigin); + } + + const int32 Pixel (int32 index) const + { + return Coords (index) [0] >> kResampleSubsampleBits; + } + + }; + +/*****************************************************************************/ + +class dng_resample_weights + { + + protected: + + uint32 fRadius; + + uint32 fWeightStep; + + AutoPtr fWeights32; + AutoPtr fWeights16; + + public: + + dng_resample_weights (); + + virtual ~dng_resample_weights (); + + void Initialize (real64 scale, + const dng_resample_function &kernel, + dng_memory_allocator &allocator); + + uint32 Radius () const + { + return fRadius; + } + + uint32 Width () const + { + return fRadius * 2; + } + + int32 Offset () const + { + return 1 - (int32) fRadius; + } + + uint32 Step () const + { + return fWeightStep; + } + + const real32 *Weights32 (uint32 fract) const + { + + DNG_ASSERT (fWeights32->Buffer (), "Weights32 is NULL"); + + return fWeights32->Buffer_real32 () + fract * fWeightStep; + + } + + const int16 *Weights16 (uint32 fract) const + { + + DNG_ASSERT (fWeights16->Buffer (), "Weights16 is NULL"); + + return fWeights16->Buffer_int16 () + fract * fWeightStep; + + } + + }; + +/*****************************************************************************/ + +const uint32 kResampleSubsampleBits2D = 5; +const uint32 kResampleSubsampleCount2D = 1 << kResampleSubsampleBits2D; +const uint32 kResampleSubsampleMask2D = kResampleSubsampleCount2D - 1; + +/*****************************************************************************/ + +class dng_resample_weights_2d + { + + protected: + + uint32 fRadius; + + uint32 fRowStep; + uint32 fColStep; + + AutoPtr fWeights32; + AutoPtr fWeights16; + + public: + + dng_resample_weights_2d (); + + virtual ~dng_resample_weights_2d (); + + void Initialize (const dng_resample_function &kernel, + dng_memory_allocator &allocator); + + uint32 Radius () const + { + return fRadius; + } + + uint32 Width () const + { + return fRadius * 2; + } + + int32 Offset () const + { + return 1 - (int32) fRadius; + } + + uint32 RowStep () const + { + return fRowStep; + } + + uint32 ColStep () const + { + return fColStep; + } + + const real32 *Weights32 (dng_point fract) const + { + + DNG_ASSERT (fWeights32->Buffer (), "Weights32 is NULL"); + + const uint32 offset = fract.v * fRowStep + fract.h * fColStep; + + return fWeights32->Buffer_real32 () + offset; + + } + + const int16 *Weights16 (dng_point fract) const + { + + DNG_ASSERT (fWeights16->Buffer (), "Weights16 is NULL"); + + const uint32 offset = fract.v * fRowStep + fract.h * fColStep; + + return fWeights16->Buffer_int16 () + offset; + + } + + }; + +/*****************************************************************************/ + +void ResampleImage (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + const dng_rect &srcBounds, + const dng_rect &dstBounds, + const dng_resample_function &kernel); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_sdk.py b/source/lib/dng_sdk/dng_sdk.py new file mode 100755 index 0000000..48d6a23 --- /dev/null +++ b/source/lib/dng_sdk/dng_sdk.py @@ -0,0 +1,192 @@ +#!/usr/bin/python + +# --- Initialization - setup paths +import sys +import os +sys.path.append( os.path.normpath(os.path.join(__file__, "..")) + '/../../SCRIPTS' ) + +from commonlib import * + +gyp_build_object = { + + "targets": [ + + { + "target_name": "dng_sdk", + + "type": "static_library", + + 'include_dirs': [ + ], + + 'defines': [ '<@(default_defines)', 'qDNGXMPFiles=0', 'qDNGXMPDocOps=0' ], + + "includes": [ + "../xmp_sdk/xmp_sdk.gypi", + ], + + "sources": [ + + "./dng_validate.cpp" , + + "./dng_1d_function.cpp" , + "./dng_1d_function.h" , + "./dng_1d_table.cpp" , + "./dng_1d_table.h" , + "./dng_abort_sniffer.cpp" , + "./dng_abort_sniffer.h" , + "./dng_area_task.cpp" , + "./dng_area_task.h" , + "./dng_assertions.h" , + "./dng_auto_ptr.h" , + "./dng_bad_pixels.cpp" , + "./dng_bad_pixels.h" , + "./dng_bottlenecks.cpp" , + "./dng_bottlenecks.h" , + "./dng_camera_profile.cpp" , + "./dng_camera_profile.h" , + "./dng_classes.h" , + "./dng_color_space.cpp" , + "./dng_color_space.h" , + "./dng_color_spec.cpp" , + "./dng_color_spec.h" , + "./dng_date_time.cpp" , + "./dng_date_time.h" , + "./dng_errors.h" , + "./dng_exceptions.cpp" , + "./dng_exceptions.h" , + "./dng_exif.cpp" , + "./dng_exif.h" , + "./dng_fast_module.h" , + "./dng_file_stream.cpp" , + "./dng_file_stream.h" , + "./dng_filter_task.cpp" , + "./dng_filter_task.h" , + "./dng_fingerprint.cpp" , + "./dng_fingerprint.h" , + "./dng_flags.h" , + "./dng_gain_map.cpp" , + "./dng_gain_map.h" , + "./dng_globals.cpp" , + "./dng_globals.h" , + "./dng_host.cpp" , + "./dng_host.h" , + "./dng_hue_sat_map.cpp" , + "./dng_hue_sat_map.h" , + "./dng_ifd.cpp" , + "./dng_ifd.h" , + "./dng_image.cpp" , + "./dng_image.h" , + "./dng_image_writer.cpp" , + "./dng_image_writer.h" , + "./dng_info.cpp" , + "./dng_info.h" , + "./dng_iptc.cpp" , + "./dng_iptc.h" , + "./dng_jpeg_image.cpp" , + "./dng_jpeg_image.h" , + "./dng_lens_correction.cpp" , + "./dng_lens_correction.h" , + "./dng_linearization_info.cpp" , + "./dng_linearization_info.h" , + "./dng_lossless_jpeg.cpp" , + "./dng_lossless_jpeg.h" , + "./dng_matrix.cpp" , + "./dng_matrix.h" , + "./dng_memory.cpp" , + "./dng_memory.h" , + "./dng_memory_stream.cpp" , + "./dng_memory_stream.h" , + "./dng_misc_opcodes.cpp" , + "./dng_misc_opcodes.h" , + "./dng_mosaic_info.cpp" , + "./dng_mosaic_info.h" , + "./dng_mutex.cpp" , + "./dng_mutex.h" , + "./dng_negative.cpp" , + "./dng_negative.h" , + "./dng_opcode_list.cpp" , + "./dng_opcode_list.h" , + "./dng_opcodes.cpp" , + "./dng_opcodes.h" , + "./dng_orientation.cpp" , + "./dng_orientation.h" , + "./dng_parse_utils.cpp" , + "./dng_parse_utils.h" , + "./dng_pixel_buffer.cpp" , + "./dng_pixel_buffer.h" , + "./dng_point.cpp" , + "./dng_point.h" , + "./dng_preview.cpp" , + "./dng_preview.h" , + "./dng_pthread.cpp" , + "./dng_pthread.h" , + "./dng_rational.cpp" , + "./dng_rational.h" , + "./dng_read_image.cpp" , + "./dng_read_image.h" , + "./dng_rect.cpp" , + "./dng_rect.h" , + "./dng_ref_counted_block.cpp" , + "./dng_ref_counted_block.h" , + "./dng_reference.cpp" , + "./dng_reference.h" , + "./dng_render.cpp" , + "./dng_render.h" , + "./dng_resample.cpp" , + "./dng_resample.h" , + "./dng_sdk_limits.h" , + "./dng_shared.cpp" , + "./dng_shared.h" , + "./dng_simple_image.cpp" , + "./dng_simple_image.h" , + "./dng_spline.cpp" , + "./dng_spline.h" , + "./dng_stream.cpp" , + "./dng_stream.h" , + "./dng_string.cpp" , + "./dng_string.h" , + "./dng_string_list.cpp" , + "./dng_string_list.h" , + "./dng_tag_codes.h" , + "./dng_tag_types.cpp" , + "./dng_tag_types.h" , + "./dng_tag_values.h" , + "./dng_temperature.cpp" , + "./dng_temperature.h" , + "./dng_tile_iterator.cpp" , + "./dng_tile_iterator.h" , + "./dng_tone_curve.cpp" , + "./dng_tone_curve.h" , + "./dng_types.h" , + "./dng_uncopyable.h" , + "./dng_utils.cpp" , + "./dng_utils.h" , + "./dng_xmp.cpp" , + "./dng_xmp.h" , + "./dng_xmp_sdk.cpp" , + "./dng_xmp_sdk.h" , + "./dng_xy_coord.cpp" , + "./dng_xy_coord.h" , + ] + } + ] +} + +from argparse import ArgumentParser +parser = ArgumentParser(conflict_handler='resolve') +parser.add_argument('-m', '--modules', default="all", help='Modules to include in build step', choices=["gpr_encoding", "gpr_decoding", "all"], ) + +args = parser.parse_args() + +if args.modules == "gpr_encoding" or args.modules == "all": + gyp_build_object['targets'][0]['defines'].append( "ENABLE_GPR_ENCODING=1" ) +else: + gyp_build_object['targets'][0]['defines'].append( "ENABLE_GPR_ENCODING=0" ) + +if args.modules == "gpr_decoding" or args.modules == "all": + gyp_build_object['targets'][0]['defines'].append( "ENABLE_GPR_DECODING=1" ) +else: + gyp_build_object['targets'][0]['defines'].append( "ENABLE_GPR_DECODING=0" ) + +print( gyp_build_object ) \ No newline at end of file diff --git a/source/lib/dng_sdk/dng_sdk_limits.h b/source/lib/dng_sdk/dng_sdk_limits.h new file mode 100644 index 0000000..e4feed7 --- /dev/null +++ b/source/lib/dng_sdk/dng_sdk_limits.h @@ -0,0 +1,78 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_sdk_limits.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/** \file + * Collection of constants detailing maximum values used in processing in the DNG SDK. + */ + +/*****************************************************************************/ + +#ifndef __dng_sdk_limits__ +#define __dng_sdk_limits__ + +/*****************************************************************************/ + +#include "dng_types.h" + +/*****************************************************************************/ + +/// The maximum number of previews (in addition to the main IFD's thumbnail) +/// that we support embedded in a DNG. + +const uint32 kMaxDNGPreviews = 20; + +/// The maximum number of SubIFDs that will be parsed. + +const uint32 kMaxSubIFDs = kMaxDNGPreviews + 1; + +/// The maximum number of chained IFDs that will be parsed. + +const uint32 kMaxChainedIFDs = 10; + +/// The maximum number of samples per pixel. + +const uint32 kMaxSamplesPerPixel = 4; + +/// Maximum number of color planes. + +const uint32 kMaxColorPlanes = kMaxSamplesPerPixel; + +/// The maximum size of a CFA repeating pattern. + +const uint32 kMaxCFAPattern = 8; + +/// The maximum size of a black level repeating pattern. + +const uint32 kMaxBlackPattern = 8; + +/// The maximum number of masked area rectangles. + +const uint32 kMaxMaskedAreas = 4; + +/// The maximum image size supported (pixels per side). + +const uint32 kMaxImageSide = 65000; + +/// Maximum number of MP threads for dng_area_task operations. + +#if qDNG64Bit +const uint32 kMaxMPThreads = 32; +#else +const uint32 kMaxMPThreads = 8; +#endif + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_shared.cpp b/source/lib/dng_sdk/dng_shared.cpp new file mode 100644 index 0000000..8c96c81 --- /dev/null +++ b/source/lib/dng_sdk/dng_shared.cpp @@ -0,0 +1,3289 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_shared.cpp#2 $ */ +/* $DateTime: 2012/06/14 20:24:41 $ */ +/* $Change: 835078 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_shared.h" + +#include "dng_camera_profile.h" +#include "dng_exceptions.h" +#include "dng_globals.h" +#include "dng_parse_utils.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_camera_profile_info::dng_camera_profile_info () + + : fBigEndian (false) + + , fColorPlanes (0) + + , fCalibrationIlluminant1 (lsUnknown) + , fCalibrationIlluminant2 (lsUnknown) + + , fColorMatrix1 () + , fColorMatrix2 () + + , fForwardMatrix1 () + , fForwardMatrix2 () + + , fReductionMatrix1 () + , fReductionMatrix2 () + + , fProfileCalibrationSignature () + + , fProfileName () + + , fProfileCopyright () + + , fEmbedPolicy (pepAllowCopying) + + , fProfileHues (0) + , fProfileSats (0) + , fProfileVals (0) + + , fHueSatDeltas1Offset (0) + , fHueSatDeltas1Count (0) + + , fHueSatDeltas2Offset (0) + , fHueSatDeltas2Count (0) + + , fHueSatMapEncoding (encoding_Linear) + + , fLookTableHues (0) + , fLookTableSats (0) + , fLookTableVals (0) + + , fLookTableOffset (0) + , fLookTableCount (0) + + , fLookTableEncoding (encoding_Linear) + + , fBaselineExposureOffset (0, 100) + + , fDefaultBlackRender (defaultBlackRender_Auto) + + , fToneCurveOffset (0) + , fToneCurveCount (0) + + , fUniqueCameraModel () + + { + + } + +/*****************************************************************************/ + +dng_camera_profile_info::~dng_camera_profile_info () + { + + } + +/*****************************************************************************/ + +bool dng_camera_profile_info::ParseTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset) + { + + switch (tagCode) + { + + case tcCalibrationIlluminant1: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCalibrationIlluminant1 = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CalibrationIlluminant1: %s\n", + LookupLightSource (fCalibrationIlluminant1)); + + } + + #endif + + break; + + } + + case tcCalibrationIlluminant2: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fCalibrationIlluminant2 = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CalibrationIlluminant2: %s\n", + LookupLightSource (fCalibrationIlluminant2)); + + } + + #endif + + break; + + } + + case tcColorMatrix1: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (fColorPlanes == 0) + { + + fColorPlanes = Pin_uint32 (0, tagCount / 3, kMaxColorPlanes); + + } + + if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + fColorPlanes, + 3, + fColorMatrix1)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ColorMatrix1:\n"); + + DumpMatrix (fColorMatrix1); + + } + + #endif + + break; + + } + + case tcColorMatrix2: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + // Kludge - Hasselblad FFF files are very DNG-like, but sometimes + // only have a ColorMatrix2 tag and no ColorMatrix1 tag. + + bool hasselbladHack = (fColorPlanes == 0); + + if (hasselbladHack) + { + + fColorPlanes = Pin_uint32 (0, tagCount / 3, kMaxColorPlanes); + + #if qDNGValidate + + ReportWarning ("ColorMatrix2 without ColorMatrix1"); + + #endif + + } + + if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + fColorPlanes, + 3, + fColorMatrix2)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ColorMatrix2:\n"); + + DumpMatrix (fColorMatrix2); + + } + + #endif + + if (hasselbladHack) + { + + fColorMatrix1 = fColorMatrix2; + + fColorMatrix2 = dng_matrix (); + + } + + break; + + } + + case tcForwardMatrix1: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + 3, + fColorPlanes, + fForwardMatrix1)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ForwardMatrix1:\n"); + + DumpMatrix (fForwardMatrix1); + + } + + #endif + + break; + + } + + case tcForwardMatrix2: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + 3, + fColorPlanes, + fForwardMatrix2)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ForwardMatrix2:\n"); + + DumpMatrix (fForwardMatrix2); + + } + + #endif + + break; + + } + + case tcReductionMatrix1: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + 3, + fColorPlanes, + fReductionMatrix1)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ReductionMatrix1:\n"); + + DumpMatrix (fReductionMatrix1); + + } + + #endif + + break; + + } + + case tcReductionMatrix2: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + 3, + fColorPlanes, + fReductionMatrix2)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ReductionMatrix2:\n"); + + DumpMatrix (fReductionMatrix2); + + } + + #endif + + break; + + } + + case tcProfileCalibrationSignature: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fProfileCalibrationSignature, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileCalibrationSignature: "); + + DumpString (fProfileCalibrationSignature); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcProfileName: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fProfileName, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileName: "); + + DumpString (fProfileName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcProfileCopyright: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fProfileCopyright, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileCopyright: "); + + DumpString (fProfileCopyright); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcProfileEmbedPolicy: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fEmbedPolicy = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + const char *policy; + + switch (fEmbedPolicy) + { + + case pepAllowCopying: + policy = "Allow copying"; + break; + + case pepEmbedIfUsed: + policy = "Embed if used"; + break; + + case pepEmbedNever: + policy = "Embed never"; + break; + + case pepNoRestrictions: + policy = "No restrictions"; + break; + + default: + policy = "INVALID VALUE"; + + } + + printf ("ProfileEmbedPolicy: %s\n", policy); + + } + + #endif + + break; + + } + + case tcProfileHueSatMapDims: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 2, 3); + + fProfileHues = stream.TagValue_uint32 (tagType); + fProfileSats = stream.TagValue_uint32 (tagType); + + if (tagCount > 2) + fProfileVals = stream.TagValue_uint32 (tagType); + else + fProfileVals = 1; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileHueSatMapDims: Hues = %u, Sats = %u, Vals = %u\n", + (unsigned) fProfileHues, + (unsigned) fProfileSats, + (unsigned) fProfileVals); + + } + + #endif + + break; + + } + + case tcProfileHueSatMapData1: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) + return false; + + bool skipSat0 = (tagCount == fProfileHues * + (fProfileSats - 1) * + fProfileVals * 3); + + if (!skipSat0) + { + + if (!CheckTagCount (parentCode, tagCode, tagCount, fProfileHues * + fProfileSats * + fProfileVals * 3)) + return false; + + } + + fBigEndian = stream.BigEndian (); + + fHueSatDeltas1Offset = tagOffset; + fHueSatDeltas1Count = tagCount; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileHueSatMapData1:\n"); + + DumpHueSatMap (stream, + fProfileHues, + fProfileSats, + fProfileVals, + skipSat0); + + } + + #endif + + break; + + } + + case tcProfileHueSatMapData2: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) + return false; + + bool skipSat0 = (tagCount == fProfileHues * + (fProfileSats - 1) * + fProfileVals * 3); + + if (!skipSat0) + { + + if (!CheckTagCount (parentCode, tagCode, tagCount, fProfileHues * + fProfileSats * + fProfileVals * 3)) + return false; + + } + + fBigEndian = stream.BigEndian (); + + fHueSatDeltas2Offset = tagOffset; + fHueSatDeltas2Count = tagCount; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileHueSatMapData2:\n"); + + DumpHueSatMap (stream, + fProfileHues, + fProfileSats, + fProfileVals, + skipSat0); + + } + + #endif + + break; + + } + + case tcProfileHueSatMapEncoding: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fHueSatMapEncoding = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + const char *encoding = NULL; + + switch (fHueSatMapEncoding) + { + + case encoding_Linear: + encoding = "Linear"; + break; + + case encoding_sRGB: + encoding = "sRGB"; + break; + + default: + encoding = "INVALID VALUE"; + + } + + printf ("ProfileHueSatMapEncoding: %s\n", encoding); + + } + + #endif + + break; + + } + + case tcProfileLookTableDims: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 2, 3); + + fLookTableHues = stream.TagValue_uint32 (tagType); + fLookTableSats = stream.TagValue_uint32 (tagType); + + if (tagCount > 2) + fLookTableVals = stream.TagValue_uint32 (tagType); + else + fLookTableVals = 1; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileLookTableDims: Hues = %u, Sats = %u, Vals = %u\n", + (unsigned) fLookTableHues, + (unsigned) fLookTableSats, + (unsigned) fLookTableVals); + + } + + #endif + + break; + + } + + case tcProfileLookTableData: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) + return false; + + bool skipSat0 = (tagCount == fLookTableHues * + (fLookTableSats - 1) * + fLookTableVals * 3); + + if (!skipSat0) + { + + if (!CheckTagCount (parentCode, tagCode, tagCount, fLookTableHues * + fLookTableSats * + fLookTableVals * 3)) + return false; + + } + + fBigEndian = stream.BigEndian (); + + fLookTableOffset = tagOffset; + fLookTableCount = tagCount; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ProfileLookTableData:\n"); + + DumpHueSatMap (stream, + fLookTableHues, + fLookTableSats, + fLookTableVals, + skipSat0); + + } + + #endif + + break; + + } + + case tcProfileLookTableEncoding: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fLookTableEncoding = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + const char *encoding = NULL; + + switch (fLookTableEncoding) + { + + case encoding_Linear: + encoding = "Linear"; + break; + + case encoding_sRGB: + encoding = "sRGB"; + break; + + default: + encoding = "INVALID VALUE"; + + } + + printf ("ProfileLookTableEncoding: %s\n", encoding); + + } + + #endif + + break; + + } + + case tcBaselineExposureOffset: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBaselineExposureOffset = stream.TagValue_srational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BaselineExposureOffset: %+0.2f\n", + fBaselineExposureOffset.As_real64 ()); + + } + + #endif + + break; + + } + + case tcDefaultBlackRender: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fDefaultBlackRender = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + const char *setting = NULL; + + switch (fDefaultBlackRender) + { + + case defaultBlackRender_Auto: + setting = "Auto"; + break; + + case defaultBlackRender_None: + setting = "None"; + break; + + default: + setting = "INVALID VALUE"; + + } + + printf ("DefaultBlackRender: %s\n", + setting); + + } + + #endif + + break; + + } + + case tcProfileToneCurve: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttFloat)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 4, tagCount)) + return false; + + if ((tagCount & 1) != 0) + { + + #if qDNGValidate + + { + + char message [256]; + + sprintf (message, + "%s %s has odd count (%u)", + LookupParentCode (parentCode), + LookupTagCode (parentCode, tagCode), + (unsigned) tagCount); + + ReportWarning (message); + + } + + #endif + + return false; + + } + + fBigEndian = stream.BigEndian (); + + fToneCurveOffset = tagOffset; + fToneCurveCount = tagCount; + + #if qDNGValidate + + if (gVerbose) + { + + DumpTagValues (stream, + "Coord", + parentCode, + tagCode, + tagType, + tagCount); + + + } + + #endif + + break; + + } + + case tcUniqueCameraModel: + { + + // Note: This code is only used when parsing stand-alone + // profiles. The embedded profiles are assumed to be restricted + // to the model they are embedded in. + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fUniqueCameraModel, + false); + + bool didTrim = fUniqueCameraModel.TrimTrailingBlanks (); + + #if qDNGValidate + + if (didTrim) + { + + ReportWarning ("UniqueCameraModel string has trailing blanks"); + + } + + if (gVerbose) + { + + printf ("UniqueCameraModel: "); + + DumpString (fUniqueCameraModel); + + printf ("\n"); + + } + + #else + + (void) didTrim; // Unused + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_camera_profile_info::ParseExtended (dng_stream &stream) + { + + try + { + + // Offsets are relative to the start of this structure, not the entire file. + + uint64 startPosition = stream.Position (); + + // Read header. Like a TIFF header, but with different magic number + // Plus all offsets are relative to the start of the IFD, not to the + // stream or file. + + uint16 byteOrder = stream.Get_uint16 (); + + if (byteOrder == byteOrderMM) + fBigEndian = true; + + else if (byteOrder == byteOrderII) + fBigEndian = false; + + else + return false; + + TempBigEndian setEndianness (stream, fBigEndian); + + uint16 magicNumber = stream.Get_uint16 (); + + if (magicNumber != magicExtendedProfile) + { + return false; + } + + uint32 offset = stream.Get_uint32 (); + + stream.Skip (offset - 8); + + // Start on IFD entries. + + uint32 ifdEntries = stream.Get_uint16 (); + + if (ifdEntries < 1) + { + return false; + } + + for (uint32 tag_index = 0; tag_index < ifdEntries; tag_index++) + { + + stream.SetReadPosition (startPosition + 8 + 2 + tag_index * 12); + + uint16 tagCode = stream.Get_uint16 (); + uint32 tagType = stream.Get_uint16 (); + uint32 tagCount = stream.Get_uint32 (); + + uint64 tagOffset = stream.Position (); + + if (TagTypeSize (tagType) * tagCount > 4) + { + + tagOffset = startPosition + stream.Get_uint32 (); + + stream.SetReadPosition (tagOffset); + + } + + if (!ParseTag (stream, + 0, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + #if qDNGValidate + + if (gVerbose) + { + + stream.SetReadPosition (tagOffset); + + printf ("*"); + + DumpTagValues (stream, + LookupTagType (tagType), + 0, + tagCode, + tagType, + tagCount); + + } + + #endif + + } + + } + + return true; + + } + + catch (...) + { + + // Eat parsing errors. + + } + + return false; + + } + +/*****************************************************************************/ + +dng_shared::dng_shared () + + : fExifIFD (0) + , fGPSInfo (0) + , fInteroperabilityIFD (0) + , fKodakDCRPrivateIFD (0) + , fKodakKDCPrivateIFD (0) + + , fXMPCount (0) + , fXMPOffset (0) + + , fIPTC_NAA_Count (0) + , fIPTC_NAA_Offset (0) + + , fMakerNoteCount (0) + , fMakerNoteOffset (0) + , fMakerNoteSafety (0) + + , fDNGVersion (0) + , fDNGBackwardVersion (0) + + , fUniqueCameraModel () + , fLocalizedCameraModel () + + , fCameraProfile () + + , fExtraCameraProfiles () + + , fCameraCalibration1 () + , fCameraCalibration2 () + + , fCameraCalibrationSignature () + + , fAnalogBalance () + + , fAsShotNeutral () + + , fAsShotWhiteXY () + + , fBaselineExposure (0, 1) + , fBaselineNoise (1, 1) + , fNoiseReductionApplied (0, 0) + , fBaselineSharpness (1, 1) + , fLinearResponseLimit (1, 1) + , fShadowScale (1, 1) + + , fHasBaselineExposure (false) + , fHasShadowScale (false) + + , fDNGPrivateDataCount (0) + , fDNGPrivateDataOffset (0) + + , fRawImageDigest () + , fNewRawImageDigest () + + , fRawDataUniqueID () + + , fOriginalRawFileName () + + , fOriginalRawFileDataCount (0) + , fOriginalRawFileDataOffset (0) + + , fOriginalRawFileDigest () + + , fAsShotICCProfileCount (0) + , fAsShotICCProfileOffset (0) + + , fAsShotPreProfileMatrix () + + , fCurrentICCProfileCount (0) + , fCurrentICCProfileOffset (0) + + , fCurrentPreProfileMatrix () + + , fColorimetricReference (crSceneReferred) + + , fAsShotProfileName () + + , fNoiseProfile () + + , fOriginalDefaultFinalSize () + , fOriginalBestQualityFinalSize () + + , fOriginalDefaultCropSizeH () + , fOriginalDefaultCropSizeV () + + { + + } + +/*****************************************************************************/ + +dng_shared::~dng_shared () + { + + } + +/*****************************************************************************/ + +bool dng_shared::ParseTag (dng_stream &stream, + dng_exif &exif, + uint32 parentCode, + bool /* isMainIFD */, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset, + int64 /* offsetDelta */) + { + + if (parentCode == 0) + { + + if (Parse_ifd0 (stream, + exif, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + if (parentCode == 0 || + parentCode == tcExifIFD) + { + + if (Parse_ifd0_exif (stream, + exif, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset)) + { + + return true; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in IFD 0. + +bool dng_shared::Parse_ifd0 (dng_stream &stream, + dng_exif & /* exif */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset) + { + + switch (tagCode) + { + + case tcXMP: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte, ttUndefined); + + fXMPCount = tagCount; + fXMPOffset = fXMPCount ? tagOffset : 0; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("XMP: Count = %u, Offset = %u\n", + (unsigned) fXMPCount, + (unsigned) fXMPOffset); + + if (fXMPCount) + { + + DumpXMP (stream, fXMPCount); + + } + + } + + #endif + + break; + + } + + case tcIPTC_NAA: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttAscii, ttUndefined); + + fIPTC_NAA_Count = tagCount * TagTypeSize (tagType); + fIPTC_NAA_Offset = fIPTC_NAA_Count ? tagOffset : 0; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("IPTC/NAA: Count = %u, Offset = %u\n", + (unsigned) fIPTC_NAA_Count, + (unsigned) fIPTC_NAA_Offset); + + if (fIPTC_NAA_Count) + { + + DumpHexAscii (stream, fIPTC_NAA_Count); + + } + + // Compute and output the digest. + + dng_memory_data buffer (fIPTC_NAA_Count); + + stream.SetReadPosition (fIPTC_NAA_Offset); + + stream.Get (buffer.Buffer (), fIPTC_NAA_Count); + + const uint8 *data = buffer.Buffer_uint8 (); + + uint32 count = fIPTC_NAA_Count; + + // Method 1: Counting all bytes (this is correct). + + { + + dng_md5_printer printer; + + printer.Process (data, count); + + printf ("IPTCDigest: "); + + DumpFingerprint (printer.Result ()); + + printf ("\n"); + + } + + // Method 2: Ignoring zero padding. + + { + + uint32 removed = 0; + + while ((removed < 3) && (count > 0) && (data [count - 1] == 0)) + { + removed++; + count--; + } + + if (removed != 0) + { + + dng_md5_printer printer; + + printer.Process (data, count); + + printf ("IPTCDigest (ignoring zero padding): "); + + DumpFingerprint (printer.Result ()); + + printf ("\n"); + + } + + } + + } + + #endif + + break; + + } + + case tcExifIFD: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fExifIFD = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("ExifIFD: %u\n", (unsigned) fExifIFD); + } + + #endif + + break; + + } + + case tcGPSInfo: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fGPSInfo = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("GPSInfo: %u\n", (unsigned) fGPSInfo); + } + + #endif + + break; + + } + + case tcKodakDCRPrivateIFD: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fKodakDCRPrivateIFD = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("KodakDCRPrivateIFD: %u\n", (unsigned) fKodakDCRPrivateIFD); + } + + #endif + + break; + + } + + case tcKodakKDCPrivateIFD: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fKodakKDCPrivateIFD = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("KodakKDCPrivateIFD: %u\n", (unsigned) fKodakKDCPrivateIFD); + } + + #endif + + break; + + } + + case tcDNGVersion: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fDNGVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + printf ("DNGVersion: %u.%u.%u.%u\n", + (unsigned) b0, + (unsigned) b1, + (unsigned) b2, + (unsigned) b3); + } + + #endif + + break; + + } + + case tcDNGBackwardVersion: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte); + + CheckTagCount (parentCode, tagCode, tagCount, 4); + + uint32 b0 = stream.Get_uint8 (); + uint32 b1 = stream.Get_uint8 (); + uint32 b2 = stream.Get_uint8 (); + uint32 b3 = stream.Get_uint8 (); + + fDNGBackwardVersion = (b0 << 24) | (b1 << 16) | (b2 << 8) | b3; + + #if qDNGValidate + + if (gVerbose) + { + printf ("DNGBackwardVersion: %u.%u.%u.%u\n", + (unsigned) b0, + (unsigned) b1, + (unsigned) b2, + (unsigned) b3); + } + + #endif + + break; + + } + + case tcUniqueCameraModel: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fUniqueCameraModel, + false); + + bool didTrim = fUniqueCameraModel.TrimTrailingBlanks (); + + #if qDNGValidate + + if (didTrim) + { + + ReportWarning ("UniqueCameraModel string has trailing blanks"); + + } + + if (gVerbose) + { + + printf ("UniqueCameraModel: "); + + DumpString (fUniqueCameraModel); + + printf ("\n"); + + } + + #else + + (void) didTrim; // Unused + + #endif + + break; + + } + + case tcLocalizedCameraModel: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fLocalizedCameraModel, + false); + + bool didTrim = fLocalizedCameraModel.TrimTrailingBlanks (); + + #if qDNGValidate + + if (didTrim) + { + + ReportWarning ("LocalizedCameraModel string has trailing blanks"); + + } + + if (gVerbose) + { + + printf ("LocalizedCameraModel: "); + + DumpString (fLocalizedCameraModel); + + printf ("\n"); + + } + + #else + + (void) didTrim; // Unused + + #endif + + break; + + } + + case tcCameraCalibration1: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + fCameraProfile.fColorPlanes, + fCameraProfile.fColorPlanes, + fCameraCalibration1)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CameraCalibration1:\n"); + + DumpMatrix (fCameraCalibration1); + + } + + #endif + + break; + + } + + case tcCameraCalibration2: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + fCameraProfile.fColorPlanes, + fCameraProfile.fColorPlanes, + fCameraCalibration2)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CameraCalibration2:\n"); + + DumpMatrix (fCameraCalibration2); + + } + + #endif + + break; + + } + + case tcCameraCalibrationSignature: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fCameraCalibrationSignature, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CameraCalibrationSignature: "); + + DumpString (fCameraCalibrationSignature); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcAnalogBalance: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + // Kludge - Hasselblad FFF files are very DNG-like, but sometimes + // they don't have any ColorMatrix tags. + + bool hasselbladHack = (fDNGVersion == 0 && + fCameraProfile.fColorPlanes == 0); + + if (hasselbladHack) + { + + fCameraProfile.fColorPlanes = Pin_uint32 (0, tagCount, kMaxColorPlanes); + + #if qDNGValidate + + ReportWarning ("AnalogBalance without ColorMatrix1"); + + #endif + + } + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + if (!ParseVectorTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + fCameraProfile.fColorPlanes, + fAnalogBalance)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AnalogBalance:"); + + DumpVector (fAnalogBalance); + + } + + #endif + + break; + + } + + case tcAsShotNeutral: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + // Kludge - Hasselblad FFF files are very DNG-like, but sometimes + // they don't have any ColorMatrix tags. + + bool hasselbladHack = (fDNGVersion == 0 && + fCameraProfile.fColorPlanes == 0); + + if (hasselbladHack) + { + + fCameraProfile.fColorPlanes = Pin_uint32 (0, tagCount, kMaxColorPlanes); + + #if qDNGValidate + + ReportWarning ("AsShotNeutral without ColorMatrix1"); + + #endif + + } + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + if (!ParseVectorTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + fCameraProfile.fColorPlanes, + fAsShotNeutral)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AsShotNeutral:"); + + DumpVector (fAsShotNeutral); + + } + + #endif + + break; + + } + + case tcAsShotWhiteXY: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fAsShotWhiteXY.x = stream.TagValue_real64 (tagType); + fAsShotWhiteXY.y = stream.TagValue_real64 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AsShotWhiteXY: %0.4f %0.4f\n", + fAsShotWhiteXY.x, + fAsShotWhiteXY.y); + + } + + #endif + + break; + + } + + case tcBaselineExposure: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBaselineExposure = stream.TagValue_srational (tagType); + + fHasBaselineExposure = true; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BaselineExposure: %+0.2f\n", + fBaselineExposure.As_real64 ()); + + } + + #endif + + break; + + } + + case tcBaselineNoise: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBaselineNoise = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BaselineNoise: %0.2f\n", + fBaselineNoise.As_real64 ()); + + } + + #endif + + break; + + } + + case tcNoiseReductionApplied: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttRational)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 1)) + return false; + + fNoiseReductionApplied = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("NoiseReductionApplied: %u/%u\n", + (unsigned) fNoiseReductionApplied.n, + (unsigned) fNoiseReductionApplied.d); + + } + + #endif + + break; + + } + + case tcNoiseProfile: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttDouble)) + return false; + + // Must be an even, positive number of doubles in a noise profile. + + if (!tagCount || (tagCount & 1)) + return false; + + // Determine number of planes (i.e., half the number of doubles). + + const uint32 numPlanes = Pin_uint32 (0, + tagCount >> 1, + kMaxColorPlanes); + + // Parse the noise function parameters. + + std::vector noiseFunctions; + + for (uint32 i = 0; i < numPlanes; i++) + { + + const real64 scale = stream.TagValue_real64 (tagType); + const real64 offset = stream.TagValue_real64 (tagType); + + noiseFunctions.push_back (dng_noise_function (scale, offset)); + + } + + // Store the noise profile. + + fNoiseProfile = dng_noise_profile (noiseFunctions); + + // Debug. + + #if qDNGValidate + + if (gVerbose) + { + + printf ("NoiseProfile:\n"); + + printf (" Planes: %u\n", (unsigned) numPlanes); + + for (uint32 plane = 0; plane < numPlanes; plane++) + { + + printf (" Noise function for plane %u: scale = %.8lf, offset = %.8lf\n", + (unsigned) plane, + noiseFunctions [plane].Scale (), + noiseFunctions [plane].Offset ()); + + } + + } + + #endif + + break; + + } + + case tcBaselineSharpness: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fBaselineSharpness = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("BaselineSharpness: %0.2f\n", + fBaselineSharpness.As_real64 ()); + + } + + #endif + + break; + + } + + case tcLinearResponseLimit: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fLinearResponseLimit = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("LinearResponseLimit: %0.2f\n", + fLinearResponseLimit.As_real64 ()); + + } + + #endif + + break; + + } + + case tcShadowScale: + { + + CheckTagType (parentCode, tagCode, tagType, ttRational); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fShadowScale = stream.TagValue_urational (tagType); + + fHasShadowScale = true; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ShadowScale: %0.4f\n", + fShadowScale.As_real64 ()); + + } + + #endif + + break; + + } + + case tcDNGPrivateData: + { + + CheckTagType (parentCode, tagCode, tagType, ttByte); + + fDNGPrivateDataCount = tagCount; + fDNGPrivateDataOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("DNGPrivateData: Count = %u, Offset = %u\n", + (unsigned) fDNGPrivateDataCount, + (unsigned) fDNGPrivateDataOffset); + + DumpHexAscii (stream, tagCount); + + } + + #endif + + break; + + } + + case tcMakerNoteSafety: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fMakerNoteSafety = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("MakerNoteSafety: %s\n", + LookupMakerNoteSafety (fMakerNoteSafety)); + + } + + #endif + + break; + + } + + case tcRawImageDigest: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) + return false; + + stream.Get (fRawImageDigest.data, 16); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("RawImageDigest: "); + + DumpFingerprint (fRawImageDigest); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcNewRawImageDigest: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) + return false; + + stream.Get (fNewRawImageDigest.data, 16); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("NewRawImageDigest: "); + + DumpFingerprint (fNewRawImageDigest); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcRawDataUniqueID: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) + return false; + + stream.Get (fRawDataUniqueID.data, 16); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("RawDataUniqueID: "); + + DumpFingerprint (fRawDataUniqueID); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcOriginalRawFileName: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fOriginalRawFileName, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OriginalRawFileName: "); + + DumpString (fOriginalRawFileName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcOriginalRawFileData: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fOriginalRawFileDataCount = tagCount; + fOriginalRawFileDataOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OriginalRawFileData: Count = %u, Offset = %u\n", + (unsigned) fOriginalRawFileDataCount, + (unsigned) fOriginalRawFileDataOffset); + + DumpHexAscii (stream, tagCount); + + } + + #endif + + break; + + } + + case tcOriginalRawFileDigest: + { + + if (!CheckTagType (parentCode, tagCode, tagType, ttByte)) + return false; + + if (!CheckTagCount (parentCode, tagCode, tagCount, 16)) + return false; + + stream.Get (fOriginalRawFileDigest.data, 16); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OriginalRawFileDigest: "); + + DumpFingerprint (fOriginalRawFileDigest); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcAsShotICCProfile: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fAsShotICCProfileCount = tagCount; + fAsShotICCProfileOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AsShotICCProfile: Count = %u, Offset = %u\n", + (unsigned) fAsShotICCProfileCount, + (unsigned) fAsShotICCProfileOffset); + + DumpHexAscii (stream, tagCount); + + } + + #endif + + break; + + } + + case tcAsShotPreProfileMatrix: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + uint32 rows = fCameraProfile.fColorPlanes; + + if (tagCount == fCameraProfile.fColorPlanes * 3) + { + rows = 3; + } + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + rows, + fCameraProfile.fColorPlanes, + fAsShotPreProfileMatrix)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AsShotPreProfileMatrix:\n"); + + DumpMatrix (fAsShotPreProfileMatrix); + + } + + #endif + + break; + + } + + case tcCurrentICCProfile: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fCurrentICCProfileCount = tagCount; + fCurrentICCProfileOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CurrentICCProfile: Count = %u, Offset = %u\n", + (unsigned) fCurrentICCProfileCount, + (unsigned) fCurrentICCProfileOffset); + + DumpHexAscii (stream, tagCount); + + } + + #endif + + break; + + } + + case tcCurrentPreProfileMatrix: + { + + CheckTagType (parentCode, tagCode, tagType, ttSRational); + + if (!CheckColorImage (parentCode, tagCode, fCameraProfile.fColorPlanes)) + return false; + + uint32 rows = fCameraProfile.fColorPlanes; + + if (tagCount == fCameraProfile.fColorPlanes * 3) + { + rows = 3; + } + + if (!ParseMatrixTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + rows, + fCameraProfile.fColorPlanes, + fCurrentPreProfileMatrix)) + return false; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("CurrentPreProfileMatrix:\n"); + + DumpMatrix (fCurrentPreProfileMatrix); + + } + + #endif + + break; + + } + + case tcColorimetricReference: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fColorimetricReference = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ColorimetricReference: %s\n", + LookupColorimetricReference (fColorimetricReference)); + + } + + #endif + + break; + + } + + case tcExtraCameraProfiles: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong); + + CheckTagCount (parentCode, tagCode, tagCount, 1, tagCount); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("ExtraCameraProfiles: %u\n", (unsigned) tagCount); + + } + + #endif + + fExtraCameraProfiles.reserve (tagCount); + + for (uint32 index = 0; index < tagCount; index++) + { + + #if qDNGValidate + + if (gVerbose) + { + + printf ("\nExtraCameraProfile [%u]:\n\n", (unsigned) index); + + } + + #endif + + stream.SetReadPosition (tagOffset + index * 4); + + uint32 profileOffset = stream.TagValue_uint32 (tagType); + + dng_camera_profile_info profileInfo; + + stream.SetReadPosition (profileOffset); + + if (profileInfo.ParseExtended (stream)) + { + + fExtraCameraProfiles.push_back (profileInfo); + + } + + else + { + + #if qDNGValidate + + ReportWarning ("Unable to parse extra camera profile"); + + #endif + + } + + } + + #if qDNGValidate + + if (gVerbose) + { + + printf ("\nDone with ExtraCameraProfiles\n\n"); + + } + + #endif + + break; + + } + + case tcAsShotProfileName: + { + + CheckTagType (parentCode, tagCode, tagType, ttAscii, ttByte); + + ParseStringTag (stream, + parentCode, + tagCode, + tagCount, + fAsShotProfileName, + false); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("AsShotProfileName: "); + + DumpString (fAsShotProfileName); + + printf ("\n"); + + } + + #endif + + break; + + } + + case tcOriginalDefaultFinalSize: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fOriginalDefaultFinalSize.h = stream.TagValue_int32 (tagType); + fOriginalDefaultFinalSize.v = stream.TagValue_int32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OriginalDefaultFinalSize: H = %d V = %d\n", + (int) fOriginalDefaultFinalSize.h, + (int) fOriginalDefaultFinalSize.v); + + } + + #endif + + break; + + } + + case tcOriginalBestQualityFinalSize: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fOriginalBestQualityFinalSize.h = stream.TagValue_int32 (tagType); + fOriginalBestQualityFinalSize.v = stream.TagValue_int32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OriginalBestQualityFinalSize: H = %d V = %d\n", + (int) fOriginalBestQualityFinalSize.h, + (int) fOriginalBestQualityFinalSize.v); + + } + + #endif + + break; + + } + + case tcOriginalDefaultCropSize: + { + + CheckTagType (parentCode, tagCode, tagType, ttShort, ttLong, ttRational); + + if (!CheckTagCount (parentCode, tagCode, tagCount, 2)) + return false; + + fOriginalDefaultCropSizeH = stream.TagValue_urational (tagType); + fOriginalDefaultCropSizeV = stream.TagValue_urational (tagType); + + #if qDNGValidate + + if (gVerbose) + { + + printf ("OriginalDefaultCropSize: H = %0.2f V = %0.2f\n", + fOriginalDefaultCropSizeH.As_real64 (), + fOriginalDefaultCropSizeV.As_real64 ()); + + } + + #endif + + break; + + } + + default: + { + + // The main camera profile tags also appear in IFD 0 + + return fCameraProfile.ParseTag (stream, + parentCode, + tagCode, + tagType, + tagCount, + tagOffset); + + } + + } + + return true; + + } + +/*****************************************************************************/ + +// Parses tags that should only appear in IFD 0 or EXIF IFD. + +bool dng_shared::Parse_ifd0_exif (dng_stream &stream, + dng_exif & /* exif */, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset) + { + + switch (tagCode) + { + + case tcMakerNote: + { + + CheckTagType (parentCode, tagCode, tagType, ttUndefined); + + fMakerNoteCount = tagCount; + fMakerNoteOffset = tagOffset; + + #if qDNGValidate + + if (gVerbose) + { + + printf ("MakerNote: Count = %u, Offset = %u\n", + (unsigned) fMakerNoteCount, + (unsigned) fMakerNoteOffset); + + DumpHexAscii (stream, tagCount); + + } + + #endif + + break; + + } + + case tcInteroperabilityIFD: + { + + CheckTagType (parentCode, tagCode, tagType, ttLong, ttIFD); + + CheckTagCount (parentCode, tagCode, tagCount, 1); + + fInteroperabilityIFD = stream.TagValue_uint32 (tagType); + + #if qDNGValidate + + if (gVerbose) + { + printf ("InteroperabilityIFD: %u\n", (unsigned) fInteroperabilityIFD); + } + + #endif + + break; + + } + + default: + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_shared::PostParse (dng_host & /* host */, + dng_exif & /* exif */) + { + + // Fill in default values for DNG images. + + if (fDNGVersion != 0) + { + + // Support for DNG versions before 1.0.0.0. + + if (fDNGVersion < dngVersion_1_0_0_0) + { + + #if qDNGValidate + + ReportWarning ("DNGVersion less than 1.0.0.0"); + + #endif + + // The CalibrationIlluminant tags were added just before + // DNG version 1.0.0.0, and were hardcoded before that. + + fCameraProfile.fCalibrationIlluminant1 = lsStandardLightA; + fCameraProfile.fCalibrationIlluminant2 = lsD65; + + fDNGVersion = dngVersion_1_0_0_0; + + } + + // Default value for DNGBackwardVersion tag. + + if (fDNGBackwardVersion == 0) + { + + fDNGBackwardVersion = fDNGVersion & 0xFFFF0000; + + } + + // Check DNGBackwardVersion value. + + if (fDNGBackwardVersion < dngVersion_1_0_0_0) + { + + #if qDNGValidate + + ReportWarning ("DNGBackwardVersion less than 1.0.0.0"); + + #endif + + fDNGBackwardVersion = dngVersion_1_0_0_0; + + } + + if (fDNGBackwardVersion > fDNGVersion) + { + + #if qDNGValidate + + ReportWarning ("DNGBackwardVersion > DNGVersion"); + + #endif + + fDNGBackwardVersion = fDNGVersion; + + } + + // Check UniqueCameraModel. + + if (fUniqueCameraModel.IsEmpty ()) + { + + #if qDNGValidate + + ReportWarning ("Missing or invalid UniqueCameraModel"); + + #endif + + fUniqueCameraModel.Set ("Digital Negative"); + + } + + // If we don't know the color depth yet, it must be a monochrome DNG. + + if (fCameraProfile.fColorPlanes == 0) + { + + fCameraProfile.fColorPlanes = 1; + + } + + // Check color info. + + if (fCameraProfile.fColorPlanes > 1) + { + + // Check illuminant pair. + + if (fCameraProfile.fColorMatrix2.NotEmpty ()) + { + + if (fCameraProfile.fCalibrationIlluminant1 == lsUnknown || + (fCameraProfile.fCalibrationIlluminant2 == lsUnknown || + (fCameraProfile.fCalibrationIlluminant1 == fCameraProfile.fCalibrationIlluminant2))) + { + + #if qDNGValidate + + ReportWarning ("Invalid CalibrationIlluminant pair"); + + #endif + + fCameraProfile.fColorMatrix2 = dng_matrix (); + + } + + } + + // If the colorimetric reference is the ICC profile PCS, then the + // data must already be white balanced. The "AsShotWhiteXY" is required + // to be the ICC Profile PCS white point. + + if (fColorimetricReference == crICCProfilePCS) + { + + if (fAsShotNeutral.NotEmpty ()) + { + + #if qDNGValidate + + ReportWarning ("AsShotNeutral not allowed for this " + "ColorimetricReference value"); + + #endif + + fAsShotNeutral.Clear (); + + } + + dng_xy_coord pcs = PCStoXY (); + + #if qDNGValidate + + if (fAsShotWhiteXY.IsValid ()) + { + + if (Abs_real64 (fAsShotWhiteXY.x - pcs.x) > 0.01 || + Abs_real64 (fAsShotWhiteXY.y - pcs.y) > 0.01) + { + + ReportWarning ("AsShotWhiteXY does not match the ICC Profile PCS"); + + } + + } + + #endif + + fAsShotWhiteXY = pcs; + + } + + else + { + + // Warn if both AsShotNeutral and AsShotWhiteXY are specified. + + if (fAsShotNeutral.NotEmpty () && fAsShotWhiteXY.IsValid ()) + { + + #if qDNGValidate + + ReportWarning ("Both AsShotNeutral and AsShotWhiteXY included"); + + #endif + + fAsShotWhiteXY = dng_xy_coord (); + + } + + // Warn if neither AsShotNeutral nor AsShotWhiteXY are specified. + + #if qDNGValidate + + if (fAsShotNeutral.IsEmpty () && !fAsShotWhiteXY.IsValid ()) + { + + ReportWarning ("Neither AsShotNeutral nor AsShotWhiteXY included", + "legal but not recommended"); + + } + + #endif + + } + + // Default values of calibration signatures are required for legacy + // compatiblity. + + if (fCameraProfile.fCalibrationIlluminant1 == lsStandardLightA && + fCameraProfile.fCalibrationIlluminant2 == lsD65 && + fCameraCalibration1.Rows () == fCameraProfile.fColorPlanes && + fCameraCalibration1.Cols () == fCameraProfile.fColorPlanes && + fCameraCalibration2.Rows () == fCameraProfile.fColorPlanes && + fCameraCalibration2.Cols () == fCameraProfile.fColorPlanes && + fCameraCalibrationSignature.IsEmpty () && + fCameraProfile.fProfileCalibrationSignature.IsEmpty () ) + { + + fCameraCalibrationSignature.Set (kAdobeCalibrationSignature); + + fCameraProfile.fProfileCalibrationSignature.Set (kAdobeCalibrationSignature); + + } + + } + + // Check BaselineNoise. + + if (fBaselineNoise.As_real64 () <= 0.0) + { + + #if qDNGValidate + + ReportWarning ("Invalid BaselineNoise"); + + #endif + + fBaselineNoise = dng_urational (1, 1); + + } + + // Check BaselineSharpness. + + if (fBaselineSharpness.As_real64 () <= 0.0) + { + + #if qDNGValidate + + ReportWarning ("Invalid BaselineSharpness"); + + #endif + + fBaselineSharpness = dng_urational (1, 1); + + } + + // Check NoiseProfile. + + if (!fNoiseProfile.IsValid () && fNoiseProfile.NumFunctions () != 0) + { + + #if qDNGValidate + + ReportWarning ("Invalid NoiseProfile"); + + #endif + + fNoiseProfile = dng_noise_profile (); + + } + + // Check LinearResponseLimit. + + if (fLinearResponseLimit.As_real64 () < 0.5 || + fLinearResponseLimit.As_real64 () > 1.0) + { + + #if qDNGValidate + + ReportWarning ("Invalid LinearResponseLimit"); + + #endif + + fLinearResponseLimit = dng_urational (1, 1); + + } + + // Check ShadowScale. + + if (fShadowScale.As_real64 () <= 0.0) + { + + #if qDNGValidate + + ReportWarning ("Invalid ShadowScale"); + + #endif + + fShadowScale = dng_urational (1, 1); + + } + + } + + } + +/*****************************************************************************/ + +bool dng_shared::IsValidDNG () + { + + // Check DNGVersion value. + + if (fDNGVersion < dngVersion_1_0_0_0) + { + + #if qDNGValidate + + if (fDNGVersion != dngVersion_None) + { + + ReportError ("Invalid DNGVersion"); + + } + + #if qDNGValidateTarget + + else + { + + ReportError ("Missing DNGVersion"); + + } + + #endif + + #endif + + return false; + + } + + // Check DNGBackwardVersion value. + + if (fDNGBackwardVersion > dngVersion_Current) + { + + #if qDNGValidate + + ReportError ("DNGBackwardVersion (or DNGVersion) is too high"); + + #endif + + ThrowUnsupportedDNG (); + + } + + // Check color transform info. + + if (fCameraProfile.fColorPlanes > 1) + { + + // CameraCalibration1 is optional, but it must be valid if present. + + if (fCameraCalibration1.Cols () != 0 || + fCameraCalibration1.Rows () != 0) + { + + if (fCameraCalibration1.Cols () != fCameraProfile.fColorPlanes || + fCameraCalibration1.Rows () != fCameraProfile.fColorPlanes) + { + + #if qDNGValidate + + ReportError ("CameraCalibration1 is wrong size"); + + #endif + + return false; + + } + + // Make sure it is invertable. + + try + { + + (void) Invert (fCameraCalibration1); + + } + + catch (...) + { + + #if qDNGValidate + + ReportError ("CameraCalibration1 is not invertable"); + + #endif + + return false; + + } + + } + + // CameraCalibration2 is optional, but it must be valid if present. + + if (fCameraCalibration2.Cols () != 0 || + fCameraCalibration2.Rows () != 0) + { + + if (fCameraCalibration2.Cols () != fCameraProfile.fColorPlanes || + fCameraCalibration2.Rows () != fCameraProfile.fColorPlanes) + { + + #if qDNGValidate + + ReportError ("CameraCalibration2 is wrong size"); + + #endif + + return false; + + } + + // Make sure it is invertable. + + try + { + + (void) Invert (fCameraCalibration2); + + } + + catch (...) + { + + #if qDNGValidate + + ReportError ("CameraCalibration2 is not invertable"); + + #endif + + return false; + + } + + } + + // Check analog balance + + dng_matrix analogBalance; + + if (fAnalogBalance.NotEmpty ()) + { + + analogBalance = fAnalogBalance.AsDiagonal (); + + try + { + + (void) Invert (analogBalance); + + } + + catch (...) + { + + #if qDNGValidate + + ReportError ("AnalogBalance is not invertable"); + + #endif + + return false; + + } + + } + + } + + return true; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_shared.h b/source/lib/dng_sdk/dng_shared.h new file mode 100644 index 0000000..b67d7a6 --- /dev/null +++ b/source/lib/dng_sdk/dng_shared.h @@ -0,0 +1,248 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_shared.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_shared__ +#define __dng_shared__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_fingerprint.h" +#include "dng_matrix.h" +#include "dng_negative.h" +#include "dng_rational.h" +#include "dng_string.h" +#include "dng_stream.h" +#include "dng_sdk_limits.h" +#include "dng_types.h" +#include "dng_xy_coord.h" + +#include + +/*****************************************************************************/ + +class dng_camera_profile_info + { + + public: + + bool fBigEndian; + + uint32 fColorPlanes; + + uint32 fCalibrationIlluminant1; + uint32 fCalibrationIlluminant2; + + dng_matrix fColorMatrix1; + dng_matrix fColorMatrix2; + + dng_matrix fForwardMatrix1; + dng_matrix fForwardMatrix2; + + dng_matrix fReductionMatrix1; + dng_matrix fReductionMatrix2; + + dng_string fProfileCalibrationSignature; + + dng_string fProfileName; + + dng_string fProfileCopyright; + + uint32 fEmbedPolicy; + + uint32 fProfileHues; + uint32 fProfileSats; + uint32 fProfileVals; + + uint64 fHueSatDeltas1Offset; + uint32 fHueSatDeltas1Count; + + uint64 fHueSatDeltas2Offset; + uint32 fHueSatDeltas2Count; + + uint32 fHueSatMapEncoding; + + uint32 fLookTableHues; + uint32 fLookTableSats; + uint32 fLookTableVals; + + uint64 fLookTableOffset; + uint32 fLookTableCount; + + uint32 fLookTableEncoding; + + dng_srational fBaselineExposureOffset; + + uint32 fDefaultBlackRender; + + uint64 fToneCurveOffset; + uint32 fToneCurveCount; + + dng_string fUniqueCameraModel; + + public: + + dng_camera_profile_info (); + + ~dng_camera_profile_info (); + + bool ParseTag (dng_stream &stream, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + bool ParseExtended (dng_stream &stream); + + }; + +/*****************************************************************************/ + +class dng_shared + { + + public: + + uint64 fExifIFD; + uint64 fGPSInfo; + uint64 fInteroperabilityIFD; + uint64 fKodakDCRPrivateIFD; + uint64 fKodakKDCPrivateIFD; + + uint32 fXMPCount; + uint64 fXMPOffset; + + uint32 fIPTC_NAA_Count; + uint64 fIPTC_NAA_Offset; + + uint32 fMakerNoteCount; + uint64 fMakerNoteOffset; + uint32 fMakerNoteSafety; + + uint32 fDNGVersion; + uint32 fDNGBackwardVersion; + + dng_string fUniqueCameraModel; + dng_string fLocalizedCameraModel; + + dng_camera_profile_info fCameraProfile; + + std::vector fExtraCameraProfiles; + + dng_matrix fCameraCalibration1; + dng_matrix fCameraCalibration2; + + dng_string fCameraCalibrationSignature; + + dng_vector fAnalogBalance; + + dng_vector fAsShotNeutral; + + dng_xy_coord fAsShotWhiteXY; + + dng_srational fBaselineExposure; + dng_urational fBaselineNoise; + dng_urational fNoiseReductionApplied; + dng_urational fBaselineSharpness; + dng_urational fLinearResponseLimit; + dng_urational fShadowScale; + + bool fHasBaselineExposure; + bool fHasShadowScale; + + uint32 fDNGPrivateDataCount; + uint64 fDNGPrivateDataOffset; + + dng_fingerprint fRawImageDigest; + dng_fingerprint fNewRawImageDigest; + + dng_fingerprint fRawDataUniqueID; + + dng_string fOriginalRawFileName; + + uint32 fOriginalRawFileDataCount; + uint64 fOriginalRawFileDataOffset; + + dng_fingerprint fOriginalRawFileDigest; + + uint32 fAsShotICCProfileCount; + uint64 fAsShotICCProfileOffset; + + dng_matrix fAsShotPreProfileMatrix; + + uint32 fCurrentICCProfileCount; + uint64 fCurrentICCProfileOffset; + + dng_matrix fCurrentPreProfileMatrix; + + uint32 fColorimetricReference; + + dng_string fAsShotProfileName; + + dng_noise_profile fNoiseProfile; + + dng_point fOriginalDefaultFinalSize; + dng_point fOriginalBestQualityFinalSize; + + dng_urational fOriginalDefaultCropSizeH; + dng_urational fOriginalDefaultCropSizeV; + + public: + + dng_shared (); + + virtual ~dng_shared (); + + virtual bool ParseTag (dng_stream &stream, + dng_exif &exif, + uint32 parentCode, + bool isMainIFD, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset, + int64 offsetDelta); + + virtual void PostParse (dng_host &host, + dng_exif &exif); + + virtual bool IsValidDNG (); + + protected: + + virtual bool Parse_ifd0 (dng_stream &stream, + dng_exif &exif, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + virtual bool Parse_ifd0_exif (dng_stream &stream, + dng_exif &exif, + uint32 parentCode, + uint32 tagCode, + uint32 tagType, + uint32 tagCount, + uint64 tagOffset); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_simple_image.cpp b/source/lib/dng_sdk/dng_simple_image.cpp new file mode 100644 index 0000000..03b5b59 --- /dev/null +++ b/source/lib/dng_sdk/dng_simple_image.cpp @@ -0,0 +1,197 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_simple_image.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_simple_image.h" + +#include "dng_memory.h" +#include "dng_orientation.h" +#include "dng_tag_types.h" + +/*****************************************************************************/ + +dng_simple_image::dng_simple_image (const dng_rect &bounds, + uint32 planes, + uint32 pixelType, + dng_memory_allocator &allocator) + + : dng_image (bounds, + planes, + pixelType) + + , fMemory () + , fBuffer () + , fAllocator (allocator) + + { + + uint32 pixelSize = TagTypeSize (pixelType); + + uint32 bytes = bounds.H () * bounds.W () * planes * pixelSize; + + fMemory.Reset (allocator.Allocate (bytes)); + + fBuffer.fArea = bounds; + + fBuffer.fPlane = 0; + fBuffer.fPlanes = planes; + + fBuffer.fRowStep = planes * bounds.W (); + fBuffer.fColStep = planes; + fBuffer.fPlaneStep = 1; + + fBuffer.fPixelType = pixelType; + fBuffer.fPixelSize = pixelSize; + + fBuffer.fData = fMemory->Buffer (); + + } + +/*****************************************************************************/ + +dng_simple_image::~dng_simple_image () + { + + } + +/*****************************************************************************/ + +dng_image * dng_simple_image::Clone () const + { + + AutoPtr result (new dng_simple_image (Bounds (), + Planes (), + PixelType (), + fAllocator)); + + result->fBuffer.CopyArea (fBuffer, + Bounds (), + 0, + Planes ()); + + return result.Release (); + + } + +/*****************************************************************************/ + +void dng_simple_image::SetPixelType (uint32 pixelType) + { + + dng_image::SetPixelType (pixelType); + + fBuffer.fPixelType = pixelType; + + } + +/*****************************************************************************/ + +void dng_simple_image::Trim (const dng_rect &r) + { + + fBounds.t = 0; + fBounds.l = 0; + + fBounds.b = r.H (); + fBounds.r = r.W (); + + fBuffer.fData = fBuffer.DirtyPixel (r.t, r.l); + + fBuffer.fArea = fBounds; + + } + +/*****************************************************************************/ + +void dng_simple_image::Rotate (const dng_orientation &orientation) + { + + int32 originH = fBounds.l; + int32 originV = fBounds.t; + + int32 colStep = fBuffer.fColStep; + int32 rowStep = fBuffer.fRowStep; + + uint32 width = fBounds.W (); + uint32 height = fBounds.H (); + + if (orientation.FlipH ()) + { + + originH += width - 1; + + colStep = -colStep; + + } + + if (orientation.FlipV ()) + { + + originV += height - 1; + + rowStep = -rowStep; + + } + + if (orientation.FlipD ()) + { + + int32 temp = colStep; + + colStep = rowStep; + rowStep = temp; + + width = fBounds.H (); + height = fBounds.W (); + + } + + fBuffer.fData = fBuffer.DirtyPixel (originV, originH); + + fBuffer.fColStep = colStep; + fBuffer.fRowStep = rowStep; + + fBounds.r = fBounds.l + width; + fBounds.b = fBounds.t + height; + + fBuffer.fArea = fBounds; + + } + +/*****************************************************************************/ + +void dng_simple_image::AcquireTileBuffer (dng_tile_buffer &buffer, + const dng_rect &area, + bool dirty) const + { + + buffer.fArea = area; + + buffer.fPlane = fBuffer.fPlane; + buffer.fPlanes = fBuffer.fPlanes; + buffer.fRowStep = fBuffer.fRowStep; + buffer.fColStep = fBuffer.fColStep; + buffer.fPlaneStep = fBuffer.fPlaneStep; + buffer.fPixelType = fBuffer.fPixelType; + buffer.fPixelSize = fBuffer.fPixelSize; + + buffer.fData = (void *) fBuffer.ConstPixel (buffer.fArea.t, + buffer.fArea.l, + buffer.fPlane); + + buffer.fDirty = dirty; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_simple_image.h b/source/lib/dng_sdk/dng_simple_image.h new file mode 100644 index 0000000..006337d --- /dev/null +++ b/source/lib/dng_sdk/dng_simple_image.h @@ -0,0 +1,82 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_simple_image.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_simple_image__ +#define __dng_simple_image__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_image.h" +#include "dng_pixel_buffer.h" + +/*****************************************************************************/ + +/// dng_image derived class with simple Trim and Rotate functionality. + +class dng_simple_image : public dng_image + { + + protected: + + dng_pixel_buffer fBuffer; + + AutoPtr fMemory; + + dng_memory_allocator &fAllocator; + + public: + + dng_simple_image (const dng_rect &bounds, + uint32 planes, + uint32 pixelType, + dng_memory_allocator &allocator); + + virtual ~dng_simple_image (); + + virtual dng_image * Clone () const; + + /// Setter for pixel type. + + virtual void SetPixelType (uint32 pixelType); + + /// Trim image data outside of given bounds. Memory is not reallocated or freed. + + virtual void Trim (const dng_rect &r); + + /// Rotate image according to orientation. + + virtual void Rotate (const dng_orientation &orientation); + + /// Get the buffer for direct processing. (Unique to dng_simple_image.) + + void GetPixelBuffer (dng_pixel_buffer &buffer) + { + buffer = fBuffer; + } + + protected: + + virtual void AcquireTileBuffer (dng_tile_buffer &buffer, + const dng_rect &area, + bool dirty) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_spline.cpp b/source/lib/dng_sdk/dng_spline.cpp new file mode 100644 index 0000000..cd47532 --- /dev/null +++ b/source/lib/dng_sdk/dng_spline.cpp @@ -0,0 +1,233 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_spline.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_spline.h" + +#include "dng_assertions.h" +#include "dng_exceptions.h" + +/******************************************************************************/ + +dng_spline_solver::dng_spline_solver () + + : X () + , Y () + , S () + + { + + } + +/******************************************************************************/ + +dng_spline_solver::~dng_spline_solver () + { + + } + +/******************************************************************************/ + +void dng_spline_solver::Reset () + { + + X.clear (); + Y.clear (); + + S.clear (); + + } + +/******************************************************************************/ + +void dng_spline_solver::Add (real64 x, real64 y) + { + + X.push_back (x); + Y.push_back (y); + + } + +/******************************************************************************/ + +void dng_spline_solver::Solve () + { + + // This code computes the unique curve such that: + // It is C0, C1, and C2 continuous + // The second derivative is zero at the end points + + int32 count = (int32) X.size (); + + DNG_ASSERT (count >= 2, "Too few points"); + + int32 start = 0; + int32 end = count; + + real64 A = X [start+1] - X [start]; + real64 B = (Y [start+1] - Y [start]) / A; + + S.resize (count); + + S [start] = B; + + int32 j; + + // Slopes here are a weighted average of the slopes + // to each of the adjcent control points. + + for (j = start + 2; j < end; ++j) + { + + real64 C = X [j] - X [j-1]; + real64 D = (Y [j] - Y [j-1]) / C; + + S [j-1] = (B * C + D * A) / (A + C); + + A = C; + B = D; + + } + + S [end-1] = 2.0 * B - S [end-2]; + S [start] = 2.0 * S [start] - S [start+1]; + + if ((end - start) > 2) + { + + std::vector E; + std::vector F; + std::vector G; + + E.resize (count); + F.resize (count); + G.resize (count); + + F [start] = 0.5; + E [end-1] = 0.5; + G [start] = 0.75 * (S [start] + S [start+1]); + G [end-1] = 0.75 * (S [end-2] + S [end-1]); + + for (j = start+1; j < end - 1; ++j) + { + + A = (X [j+1] - X [j-1]) * 2.0; + + E [j] = (X [j+1] - X [j]) / A; + F [j] = (X [j] - X [j-1]) / A; + G [j] = 1.5 * S [j]; + + } + + for (j = start+1; j < end; ++j) + { + + A = 1.0 - F [j-1] * E [j]; + + if (j != end-1) F [j] /= A; + + G [j] = (G [j] - G [j-1] * E [j]) / A; + + } + + for (j = end - 2; j >= start; --j) + G [j] = G [j] - F [j] * G [j+1]; + + for (j = start; j < end; ++j) + S [j] = G [j]; + + } + + } + +/******************************************************************************/ + +bool dng_spline_solver::IsIdentity () const + { + + int32 count = (int32) X.size (); + + if (count != 2) + return false; + + if (X [0] != 0.0 || X [1] != 1.0 || + Y [0] != 0.0 || Y [1] != 1.0) + return false; + + return true; + + } + +/******************************************************************************/ + +real64 dng_spline_solver::Evaluate (real64 x) const + { + + int32 count = (int32) X.size (); + + // Check for off each end of point list. + + if (x <= X [0]) + return Y [0]; + + if (x >= X [count-1]) + return Y [count-1]; + + // Binary search for the index. + + int32 lower = 1; + int32 upper = count - 1; + + while (upper > lower) + { + + int32 mid = (lower + upper) >> 1; + + if (x == X [mid]) + { + return Y [mid]; + } + + if (x > X [mid]) + lower = mid + 1; + else + upper = mid; + + } + + DNG_ASSERT (upper == lower, "Binary search error in point list"); + + int32 j = lower; + + // X [j - 1] < x <= X [j] + // A is the distance between the X [j] and X [j - 1] + // B and C describe the fractional distance to either side. B + C = 1. + + // We compute a cubic spline between the two points with slopes + // S[j-1] and S[j] at either end. Specifically, we compute the 1-D Bezier + // with control values: + // + // Y[j-1], Y[j-1] + S[j-1]*A, Y[j]-S[j]*A, Y[j] + + return EvaluateSplineSegment (x, + X [j - 1], + Y [j - 1], + S [j - 1], + X [j ], + Y [j ], + S [j ]); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_spline.h b/source/lib/dng_sdk/dng_spline.h new file mode 100644 index 0000000..d2ccdf3 --- /dev/null +++ b/source/lib/dng_sdk/dng_spline.h @@ -0,0 +1,83 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_spline.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_spline__ +#define __dng_spline__ + +/*****************************************************************************/ + +#include "dng_1d_function.h" + +#include + +/*****************************************************************************/ + +inline real64 EvaluateSplineSegment (real64 x, + real64 x0, + real64 y0, + real64 s0, + real64 x1, + real64 y1, + real64 s1) + { + + real64 A = x1 - x0; + + real64 B = (x - x0) / A; + + real64 C = (x1 - x) / A; + + real64 D = ((y0 * (2.0 - C + B) + (s0 * A * B)) * (C * C)) + + ((y1 * (2.0 - B + C) - (s1 * A * C)) * (B * B)); + + return D; + + } + +/*****************************************************************************/ + +class dng_spline_solver: public dng_1d_function + { + + protected: + + std::vector X; + std::vector Y; + + std::vector S; + + public: + + dng_spline_solver (); + + virtual ~dng_spline_solver (); + + void Reset (); + + void Add (real64 x, real64 y); + + virtual void Solve (); + + virtual bool IsIdentity () const; + + virtual real64 Evaluate (real64 x) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_stream.cpp b/source/lib/dng_sdk/dng_stream.cpp new file mode 100644 index 0000000..d2d6b96 --- /dev/null +++ b/source/lib/dng_sdk/dng_stream.cpp @@ -0,0 +1,1221 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_stream.cpp#2 $ */ +/* $DateTime: 2012/06/01 07:28:57 $ */ +/* $Change: 832715 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_stream.h" + +#include "dng_abort_sniffer.h" +#include "dng_auto_ptr.h" +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_memory.h" +#include "dng_tag_types.h" + +#include "stdc_includes.h" + +/*****************************************************************************/ + +dng_stream::dng_stream (dng_abort_sniffer *sniffer, + uint32 bufferSize, + uint64 offsetInOriginalFile) + + : fSwapBytes (false) + , fHaveLength (false) + , fLength (0) + , fOffsetInOriginalFile (offsetInOriginalFile) + , fPosition (0) + , fMemBlock (bufferSize) + , fBuffer (fMemBlock.Buffer_uint8 ()) + , fBufferSize (bufferSize) + , fBufferStart (0) + , fBufferEnd (0) + , fBufferLimit (bufferSize) + , fBufferDirty (false) + , fSniffer (sniffer) + + { + + } + +/*****************************************************************************/ + +dng_stream::dng_stream (const void *data, + uint32 count, + uint64 offsetInOriginalFile) + + : fSwapBytes (false) + , fHaveLength (true) + , fLength (count) + , fOffsetInOriginalFile (offsetInOriginalFile) + , fPosition (0) + , fMemBlock () + , fBuffer ((uint8 *) data) + , fBufferSize (count) + , fBufferStart (0) + , fBufferEnd (count) + , fBufferLimit (count) + , fBufferDirty (false) + , fSniffer (NULL) + + { + + } + +/*****************************************************************************/ + +dng_stream::~dng_stream () + { + + } + +/*****************************************************************************/ + +uint64 dng_stream::DoGetLength () + { + + ThrowProgramError (); + + return 0; + + } + +/*****************************************************************************/ + +void dng_stream::DoRead (void * /* data */, + uint32 /* count */, + uint64 /* offset */) + { + + ThrowProgramError (); + + } + +/*****************************************************************************/ + +void dng_stream::DoSetLength (uint64 /* length */) + { + + ThrowProgramError (); + + } + +/*****************************************************************************/ + +void dng_stream::DoWrite (const void * /* data */, + uint32 /* count */, + uint64 /* offset */) + { + + ThrowProgramError (); + + } + +/*****************************************************************************/ + +bool dng_stream::BigEndian () const + { + + return fSwapBytes != (!!qDNGBigEndian); + + } + +/*****************************************************************************/ + +void dng_stream::SetBigEndian (bool bigEndian) + { + + fSwapBytes = (bigEndian != (!!qDNGBigEndian)); + + } + +/*****************************************************************************/ + +const void * dng_stream::Data () const + { + + if (fBufferStart == 0 && fHaveLength && fBufferEnd == fLength) + { + + return fBuffer; + + } + + return NULL; + + } + +/*****************************************************************************/ + +dng_memory_block * dng_stream::AsMemoryBlock (dng_memory_allocator &allocator) + { + + Flush (); + + uint64 len64 = Length (); + + if (len64 > 0xFFFFFFFF) + { + ThrowProgramError (); + } + + uint32 len = (uint32) len64; + + AutoPtr block (allocator.Allocate (len)); + + if (len) + { + + SetReadPosition (0); + + Get (block->Buffer (), len); + + } + + return block.Release (); + + } + +/*****************************************************************************/ + +void dng_stream::SetReadPosition (uint64 offset) + { + + fPosition = offset; + + if (fPosition > Length ()) + { + + ThrowEndOfFile (); + + } + + } + +/*****************************************************************************/ + +uint64 dng_stream::OffsetInOriginalFile () const + { + + return fOffsetInOriginalFile; + + } + +/*****************************************************************************/ + +uint64 dng_stream::PositionInOriginalFile () const + { + + if (fOffsetInOriginalFile == kDNGStreamInvalidOffset) + return kDNGStreamInvalidOffset; + + return fOffsetInOriginalFile + Position (); + + } + +/*****************************************************************************/ + +void dng_stream::Get (void *data, uint32 count) + { + + while (count) + { + + // See if the request is totally inside buffer. + + if (fPosition >= fBufferStart && fPosition + count <= fBufferEnd) + { + + DoCopyBytes (fBuffer + (uint32) (fPosition - fBufferStart), + data, + count); + + fPosition += count; + + return; + + } + + // See if first part of request is inside buffer. + + if (fPosition >= fBufferStart && fPosition < fBufferEnd) + { + + uint32 block = (uint32) (fBufferEnd - fPosition); + + DoCopyBytes (fBuffer + (fPosition - fBufferStart), + data, + block); + + count -= block; + + data = (void *) (((char *) data) + block); + + fPosition += block; + + } + + // Flush buffer if dirty. + + Flush (); + + // Do large reads unbuffered. + + if (count > fBufferSize) + { + + if (fPosition + count > Length ()) + { + + ThrowEndOfFile (); + + } + + DoRead (data, + count, + fPosition); + + fPosition += count; + + return; + + } + + // Figure out new buffer range. + + fBufferStart = fPosition; + + if (fBufferSize >= 4096) + { + + // Align to a 4K file block. + + fBufferStart &= (uint64) ~((int64) 4095); + + } + + fBufferEnd = Min_uint64 (fBufferStart + fBufferSize, Length ()); + + if (fBufferEnd <= fPosition) + { + + ThrowEndOfFile (); + + } + + // Read data into buffer. + + dng_abort_sniffer::SniffForAbort (fSniffer); + + DoRead (fBuffer, + (uint32) (fBufferEnd - fBufferStart), + fBufferStart); + + } + + } + +/*****************************************************************************/ + +void dng_stream::SetWritePosition (uint64 offset) + { + + fPosition = offset; + + } + +/*****************************************************************************/ + +void dng_stream::Flush () + { + + if (fBufferDirty) + { + + dng_abort_sniffer::SniffForAbort (fSniffer); + + DoWrite (fBuffer, + (uint32) (fBufferEnd - fBufferStart), + fBufferStart); + + fBufferStart = 0; + fBufferEnd = 0; + fBufferLimit = fBufferSize; + + fBufferDirty = false; + + } + + } + +/*****************************************************************************/ + +void dng_stream::SetLength (uint64 length) + { + + Flush (); + + if (Length () != length) + { + + DoSetLength (length); + + fLength = length; + + } + + } + +/*****************************************************************************/ + +void dng_stream::Put (const void *data, + uint32 count) + { + + // See if we can replace or append to the existing buffer. + + uint64 endPosition = fPosition + count; + + if (fBufferDirty && + fPosition >= fBufferStart && + fPosition <= fBufferEnd && + endPosition <= fBufferLimit) + { + + DoCopyBytes (data, + fBuffer + (uint32) (fPosition - fBufferStart), + count); + + if (fBufferEnd < endPosition) + fBufferEnd = endPosition; + + } + + // Else we need to write to the file. + + else + { + + // Write existing buffer. + + Flush (); + + // Write large blocks unbuffered. + + if (count >= fBufferSize) + { + + dng_abort_sniffer::SniffForAbort (fSniffer); + + DoWrite (data, count, fPosition); + + } + + // Start a new buffer with small blocks. + + else + { + + fBufferDirty = true; + + fBufferStart = fPosition; + fBufferEnd = endPosition; + fBufferLimit = fBufferStart + fBufferSize; + + DoCopyBytes (data, + fBuffer, + count); + + } + + } + + fPosition = endPosition; + + fLength = Max_uint64 (Length (), fPosition); + + } + +/*****************************************************************************/ + +uint16 dng_stream::Get_uint16 () + { + + uint16 x; + + Get (&x, 2); + + if (fSwapBytes) + { + + x = SwapBytes16 (x); + + } + + return x; + + } + +/*****************************************************************************/ + +void dng_stream::Put_uint16 (uint16 x) + { + + if (fSwapBytes) + { + + x = SwapBytes16 (x); + + } + + Put (&x, 2); + + } + +/*****************************************************************************/ + +uint32 dng_stream::Get_uint32 () + { + + uint32 x; + + Get (&x, 4); + + if (fSwapBytes) + { + + x = SwapBytes32 (x); + + } + + return x; + + } + +/*****************************************************************************/ + +void dng_stream::Put_uint32 (uint32 x) + { + + if (fSwapBytes) + { + + x = SwapBytes32 (x); + + } + + Put (&x, 4); + + } + +/*****************************************************************************/ + +uint64 dng_stream::Get_uint64 () + { + + if (fSwapBytes) + { + + union + { + uint32 u32 [2]; + uint64 u64; + } u; + + u.u32 [1] = Get_uint32 (); + u.u32 [0] = Get_uint32 (); + + return u.u64; + + } + + uint64 x; + + Get (&x, 8); + + return x; + + } + +/*****************************************************************************/ + +void dng_stream::Put_uint64 (uint64 x) + { + + if (fSwapBytes) + { + + union + { + uint32 u32 [2]; + uint64 u64; + } u; + + u.u64 = x; + + Put_uint32 (u.u32 [1]); + Put_uint32 (u.u32 [0]); + + } + + else + { + + Put (&x, 8); + + } + + } + +/*****************************************************************************/ + +real32 dng_stream::Get_real32 () + { + + union + { + uint32 i; + real32 r; + } u; + + u.i = Get_uint32 (); + + return u.r; + + } + +/*****************************************************************************/ + +void dng_stream::Put_real32 (real32 x) + { + + if (fSwapBytes) + { + + union + { + uint32 i; + real32 r; + } u; + + u.r = x; + + Put_uint32 (u.i); + + } + + else + { + + Put (&x, 4); + + } + + } + +/*****************************************************************************/ + +real64 dng_stream::Get_real64 () + { + + if (fSwapBytes) + { + + union + { + uint32 i [2]; + real64 r; + } u; + + u.i [1] = Get_uint32 (); + u.i [0] = Get_uint32 (); + + return u.r; + + } + + real64 x; + + Get (&x, 8); + + return x; + + } + +/*****************************************************************************/ + +void dng_stream::Put_real64 (real64 x) + { + + if (fSwapBytes) + { + + union + { + uint32 i [2]; + real64 r; + } u; + + u.r = x; + + Put_uint32 (u.i [1]); + Put_uint32 (u.i [0]); + + } + + else + { + + Put (&x, 8); + + } + + } + +/*****************************************************************************/ + +void dng_stream::Get_CString (char *data, uint32 maxLength) + { + + memset (data, 0, maxLength); + + uint32 index = 0; + + while (true) + { + + char c = (char) Get_uint8 (); + + if (index + 1 < maxLength) + data [index++] = c; + + if (c == 0) + break; + + } + + } + +/*****************************************************************************/ + +void dng_stream::Get_UString (char *data, uint32 maxLength) + { + + memset (data, 0, maxLength); + + uint32 index = 0; + + while (true) + { + + char c = (char) Get_uint16 (); + + if (index + 1 < maxLength) + data [index++] = (char) c; + + if (c == 0) + break; + + } + + } + +/*****************************************************************************/ + +void dng_stream::PutZeros (uint64 count) + { + + const uint32 kZeroBufferSize = 4096; + + if (count >= kZeroBufferSize) + { + + dng_memory_data zeroBuffer (kZeroBufferSize); + + DoZeroBytes (zeroBuffer.Buffer (), + kZeroBufferSize); + + while (count) + { + + uint64 blockSize = Min_uint64 (count, kZeroBufferSize); + + Put (zeroBuffer.Buffer (), (uint32) blockSize); + + count -= blockSize; + + } + + } + + else + { + + uint32 count32 = (uint32) count; + + for (uint32 j = 0; j < count32; j++) + { + + Put_uint8 (0); + + } + + } + + } + +/*****************************************************************************/ + +void dng_stream::PadAlign2 () + { + + PutZeros (Position () & 1); + + } + +/*****************************************************************************/ + +void dng_stream::PadAlign4 () + { + + PutZeros ((4 - (Position () & 3)) & 3); + + } + +/*****************************************************************************/ + +uint32 dng_stream::TagValue_uint32 (uint32 tagType) + { + + switch (tagType) + { + + case ttByte: + return (uint32) Get_uint8 (); + + case ttShort: + return (uint32) Get_uint16 (); + + case ttLong: + case ttIFD: + return Get_uint32 (); + + } + + real64 x = TagValue_real64 (tagType); + + if (x < 0.0) + x = 0.0; + + if (x > (real64) 0xFFFFFFFF) + x = (real64) 0xFFFFFFFF; + + return (uint32) (x + 0.5); + + } + +/*****************************************************************************/ + +int32 dng_stream::TagValue_int32 (uint32 tagType) + { + + switch (tagType) + { + + case ttSByte: + return (int32) Get_int8 (); + + case ttSShort: + return (int32) Get_int16 (); + + case ttSLong: + return Get_int32 (); + + } + + real64 x = TagValue_real64 (tagType); + + if (x < 0.0) + { + + if (x < -2147483648.0) + x = -2147483648.0; + + return (int32) (x - 0.5); + + } + + else + { + + if (x > 2147483647.0) + x = 2147483647.0; + + return (int32) (x + 0.5); + + } + + } + +/*****************************************************************************/ + +dng_urational dng_stream::TagValue_urational (uint32 tagType) + { + + dng_urational result; + + result.n = 0; + result.d = 1; + + switch (tagType) + { + + case ttRational: + { + + result.n = Get_uint32 (); + result.d = Get_uint32 (); + + break; + + } + + case ttSRational: + { + + int32 n = Get_int32 (); + int32 d = Get_int32 (); + + if ((n < 0) == (d < 0)) + { + + if (d < 0) + { + n = -n; + d = -d; + } + + result.n = (uint32) n; + result.d = (uint32) d; + + } + + break; + + } + + case ttByte: + case ttShort: + case ttLong: + case ttIFD: + { + + result.n = TagValue_uint32 (tagType); + + break; + + } + + case ttSByte: + case ttSShort: + case ttSLong: + { + + int32 n = TagValue_int32 (tagType); + + if (n > 0) + { + result.n = (uint32) n; + } + + break; + + } + + default: + { + + real64 x = TagValue_real64 (tagType); + + if (x > 0.0) + { + + while (result.d < 10000 && x < 1000000) + { + + result.d *= 10; + + x *= 10.0; + + } + + result.n = (uint32) (x + 0.5); + + } + + } + + } + + return result; + + } + +/*****************************************************************************/ + +dng_srational dng_stream::TagValue_srational (uint32 tagType) + { + + dng_srational result; + + result.n = 0; + result.d = 1; + + switch (tagType) + { + + case ttSRational: + { + + result.n = Get_int32 (); + result.d = Get_int32 (); + + break; + + } + + default: + { + + real64 x = TagValue_real64 (tagType); + + if (x > 0.0) + { + + while (result.d < 10000 && x < 1000000.0) + { + + result.d *= 10; + + x *= 10.0; + + } + + result.n = (int32) (x + 0.5); + + } + + else + { + + while (result.d < 10000 && x > -1000000.0) + { + + result.d *= 10; + + x *= 10.0; + + } + + result.n = (int32) (x - 0.5); + + } + + } + + } + + return result; + + } + +/*****************************************************************************/ + +real64 dng_stream::TagValue_real64 (uint32 tagType) + { + + switch (tagType) + { + + case ttByte: + case ttShort: + case ttLong: + case ttIFD: + return (real64) TagValue_uint32 (tagType); + + case ttSByte: + case ttSShort: + case ttSLong: + return (real64) TagValue_int32 (tagType); + + case ttRational: + { + + uint32 n = Get_uint32 (); + uint32 d = Get_uint32 (); + + if (d == 0) + return 0.0; + else + return (real64) n / (real64) d; + + } + + case ttSRational: + { + + int32 n = Get_int32 (); + int32 d = Get_int32 (); + + if (d == 0) + return 0.0; + else + return (real64) n / (real64) d; + + } + + case ttFloat: + return (real64) Get_real32 (); + + case ttDouble: + return Get_real64 (); + + } + + return 0.0; + + } + +/*****************************************************************************/ + +void dng_stream::CopyToStream (dng_stream &dstStream, + uint64 count) + { + + uint8 smallBuffer [1024]; + + if (count <= sizeof (smallBuffer)) + { + + Get (smallBuffer, (uint32) count); + + dstStream.Put (smallBuffer, (uint32) count); + + } + + else + { + + const uint32 bigBufferSize = (uint32) Min_uint64 (kBigBufferSize, + count); + + dng_memory_data bigBuffer (bigBufferSize); + + while (count) + { + + uint32 blockCount = (uint32) Min_uint64 (bigBufferSize, + count); + + Get (bigBuffer.Buffer (), + blockCount); + + dstStream.Put (bigBuffer.Buffer (), + blockCount); + + count -= blockCount; + + } + + } + + } + +/*****************************************************************************/ + +void dng_stream::DuplicateStream (dng_stream &dstStream) + { + + // Turn off sniffers for this operation. + + TempStreamSniffer noSniffer1 (*this , NULL); + TempStreamSniffer noSniffer2 (dstStream, NULL); + + // First grow the destination stream if required, in an attempt to + // reserve any needed space before overwriting the existing data. + + if (dstStream.Length () < Length ()) + { + dstStream.SetLength (Length ()); + } + + SetReadPosition (0); + + dstStream.SetWritePosition (0); + + CopyToStream (dstStream, Length ()); + + dstStream.Flush (); + + dstStream.SetLength (Length ()); + + } + +/*****************************************************************************/ + +TempBigEndian::TempBigEndian (dng_stream &stream, + bool bigEndian) + + : fStream (stream) + , fOldSwap (stream.SwapBytes ()) + + { + + fStream.SetBigEndian (bigEndian); + + } + +/*****************************************************************************/ + +TempBigEndian::~TempBigEndian () + { + + fStream.SetSwapBytes (fOldSwap); + + } + +/*****************************************************************************/ + +TempStreamSniffer::TempStreamSniffer (dng_stream &stream, + dng_abort_sniffer *sniffer) + + : fStream (stream) + , fOldSniffer (stream.Sniffer ()) + + { + + fStream.SetSniffer (sniffer); + + } + +/*****************************************************************************/ + +TempStreamSniffer::~TempStreamSniffer () + { + + fStream.SetSniffer (fOldSniffer); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_stream.h b/source/lib/dng_sdk/dng_stream.h new file mode 100644 index 0000000..df233ae --- /dev/null +++ b/source/lib/dng_sdk/dng_stream.h @@ -0,0 +1,698 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_stream.h#2 $ */ +/* $DateTime: 2012/06/01 07:28:57 $ */ +/* $Change: 832715 $ */ +/* $Author: tknoll $ */ + +/** Data stream abstraction for serializing and deserializing sequences of + * basic types and RAW image data. + */ + +/*****************************************************************************/ + +#ifndef __dng_stream__ +#define __dng_stream__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" +#include "dng_memory.h" +#include "dng_rational.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +// Constants for invalid offset in streams. + +const uint64 kDNGStreamInvalidOffset = (uint64) (int64) -1; + +/*****************************************************************************/ + +/// Base stream abstraction. Has support for going between stream and pointer +/// abstraction. + +class dng_stream + { + + public: + + enum + { + + kSmallBufferSize = 4 * 1024, + kBigBufferSize = 64 * 1024, + + kDefaultBufferSize = kSmallBufferSize + + }; + + private: + + bool fSwapBytes; + + bool fHaveLength; + + uint64 fLength; + + const uint64 fOffsetInOriginalFile; + + uint64 fPosition; + + dng_memory_data fMemBlock; + + uint8 *fBuffer; + + uint32 fBufferSize; + + uint64 fBufferStart; + uint64 fBufferEnd; + uint64 fBufferLimit; + + bool fBufferDirty; + + dng_abort_sniffer *fSniffer; + + protected: + + dng_stream (dng_abort_sniffer *sniffer = NULL, + uint32 bufferSize = kDefaultBufferSize, + uint64 offsetInOriginalFile = kDNGStreamInvalidOffset); + + virtual uint64 DoGetLength (); + + virtual void DoRead (void *data, + uint32 count, + uint64 offset); + + virtual void DoSetLength (uint64 length); + + virtual void DoWrite (const void *data, + uint32 count, + uint64 offset); + + public: + + /// Construct a stream with initial data. + /// \param data Pointer to initial contents of stream. + /// \param count Number of bytes data is valid for. + /// \param offsetInOriginalFile If data came from a file originally, + /// offset can be saved here for later use. + + dng_stream (const void *data, + uint32 count, + uint64 offsetInOriginalFile = kDNGStreamInvalidOffset); + + virtual ~dng_stream (); + + /// Getter for whether stream is swapping byte order on input/output. + /// \retval If true, data will be swapped on input/output. + + bool SwapBytes () const + { + return fSwapBytes; + } + + /// Setter for whether stream is swapping byte order on input/output. + /// \param swapBytes If true, stream will swap byte order on input or + /// output for future reads/writes. + + void SetSwapBytes (bool swapBytes) + { + fSwapBytes = swapBytes; + } + + /// Getter for whether data in stream is big endian. + /// \retval If true, data in stream is big endian. + + bool BigEndian () const; + + /// Setter for whether data in stream is big endian. + /// \param bigEndian If true, data in stream is big endian. + + void SetBigEndian (bool bigEndian = true); + + /// Getter for whether data in stream is big endian. + /// \retval If true, data in stream is big endian. + + bool LittleEndian () const + { + return !BigEndian (); + } + + /// Setter for whether data in stream is big endian. + /// \param littleEndian If true, data in stream is big endian. + + void SetLittleEndian (bool littleEndian = true) + { + SetBigEndian (!littleEndian); + } + + /// Returns the size of the buffer used by the stream. + + uint32 BufferSize () const + { + return fBufferSize; + } + + /// Getter for length of data in stream. + /// \retval Length of readable data in stream. + + uint64 Length () + { + + if (!fHaveLength) + { + + fLength = DoGetLength (); + + fHaveLength = true; + + } + + return fLength; + + } + + /// Getter for current offset in stream. + /// \retval current offset from start of stream. + + uint64 Position () const + { + return fPosition; + } + + /// Getter for current position in original file, taking into account + /// OffsetInOriginalFile stream data was taken from. + /// \retval kInvalidOffset if no offset in original file is set, sum + /// of offset in original file and current position otherwise. + + uint64 PositionInOriginalFile () const; + + /// Getter for offset in original file. + /// \retval kInvalidOffset if no offset in original file is set, + /// offset in original file otherwise. + + uint64 OffsetInOriginalFile () const; + + /// Return pointer to stream contents if the stream is entirely + /// available as a single memory block, NULL otherwise. + + const void * Data () const; + + /// Return the entire stream as a single memory block. + /// This works for all streams, but requires copying the data to a new buffer. + /// \param allocator Allocator used to allocate memory. + + dng_memory_block * AsMemoryBlock (dng_memory_allocator &allocator); + + /// Seek to a new position in stream for reading. + + void SetReadPosition (uint64 offset); + + /// Skip forward in stream. + /// \param delta Number of bytes to skip forward. + + void Skip (uint64 delta) + { + SetReadPosition (Position () + delta); + } + + /// Get data from stream. Exception is thrown and no data is read if + /// insufficient data available in stream. + /// \param data Buffer to put data into. Must be valid for count bytes. + /// \param count Bytes of data to read. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + void Get (void *data, uint32 count); + + /// Seek to a new position in stream for writing. + + void SetWritePosition (uint64 offset); + + /// Force any stored data in stream to be written to underlying storage. + + void Flush (); + + /// Set length of available data. + /// \param length Number of bytes of avialble data in stream. + + void SetLength (uint64 length); + + /// Write data to stream. + /// \param data Buffer of data to write to stream. + /// \param count Bytes of in data. + + void Put (const void *data, uint32 count); + + /// Get an unsigned 8-bit integer from stream and advance read position. + /// \retval One unsigned 8-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + uint8 Get_uint8 () + { + + // Fast check to see if in buffer + + if (fPosition >= fBufferStart && fPosition < fBufferEnd) + { + + return fBuffer [fPosition++ - fBufferStart]; + + } + + // Not in buffer, let main routine do the work. + + uint8 x; + + Get (&x, 1); + + return x; + + } + + /// Put an unsigned 8-bit integer to stream and advance write position. + /// \param x One unsigned 8-bit integer. + + void Put_uint8 (uint8 x) + { + + if (fBufferDirty && + fPosition >= fBufferStart && + fPosition <= fBufferEnd && + fPosition < fBufferLimit) + { + + fBuffer [fPosition - fBufferStart] = x; + + fPosition++; + + if (fBufferEnd < fPosition) + fBufferEnd = fPosition; + + fLength = Max_uint64 (Length (), fPosition); + + } + + else + { + + Put (&x, 1); + + } + + } + + /// Get an unsigned 16-bit integer from stream and advance read position. + /// Byte swap if byte swapping is turned on. + /// \retval One unsigned 16-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + uint16 Get_uint16 (); + + /// Put an unsigned 16-bit integer to stream and advance write position. + /// Byte swap if byte swapping is turned on. + /// \param x One unsigned 16-bit integer. + + void Put_uint16 (uint16 x); + + /// Get an unsigned 32-bit integer from stream and advance read position. + /// Byte swap if byte swapping is turned on. + /// \retval One unsigned 32-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + uint32 Get_uint32 (); + + /// Put an unsigned 32-bit integer to stream and advance write position. + /// Byte swap if byte swapping is turned on. + /// \param x One unsigned 32-bit integer. + + void Put_uint32 (uint32 x); + + /// Get an unsigned 64-bit integer from stream and advance read position. + /// Byte swap if byte swapping is turned on. + /// \retval One unsigned 64-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + uint64 Get_uint64 (); + + /// Put an unsigned 64-bit integer to stream and advance write position. + /// Byte swap if byte swapping is turned on. + /// \param x One unsigned 64-bit integer. + + void Put_uint64 (uint64 x); + + /// Get one 8-bit integer from stream and advance read position. + /// \retval One 8-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + int8 Get_int8 () + { + return (int8) Get_uint8 (); + } + + /// Put one 8-bit integer to stream and advance write position. + /// \param x One 8-bit integer. + + void Put_int8 (int8 x) + { + Put_uint8 ((uint8) x); + } + + /// Get one 16-bit integer from stream and advance read position. + /// Byte swap if byte swapping is turned on. + /// \retval One 16-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + int16 Get_int16 () + { + return (int16) Get_uint16 (); + } + + /// Put one 16-bit integer to stream and advance write position. + /// Byte swap if byte swapping is turned on. + /// \param x One 16-bit integer. + + void Put_int16 (int16 x) + { + Put_uint16 ((uint16) x); + } + + /// Get one 32-bit integer from stream and advance read position. + /// Byte swap if byte swapping is turned on. + /// \retval One 32-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + int32 Get_int32 () + { + return (int32) Get_uint32 (); + } + + /// Put one 32-bit integer to stream and advance write position. + /// Byte swap if byte swapping is turned on. + /// \param x One 32-bit integer. + + void Put_int32 (int32 x) + { + Put_uint32 ((uint32) x); + } + + /// Get one 64-bit integer from stream and advance read position. + /// Byte swap if byte swapping is turned on. + /// \retval One 64-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + int64 Get_int64 () + { + return (int64) Get_uint64 (); + } + + /// Put one 64-bit integer to stream and advance write position. + /// Byte swap if byte swapping is turned on. + /// \param x One 64-bit integer. + + void Put_int64 (int64 x) + { + Put_uint64 ((uint64) x); + } + + /// Get one 32-bit IEEE floating-point number from stream and advance + /// read position. Byte swap if byte swapping is turned on. + /// \retval One 32-bit IEEE floating-point number. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + real32 Get_real32 (); + + /// Put one 32-bit IEEE floating-point number to stream and advance write + /// position. Byte swap if byte swapping is turned on. + /// \param x One 32-bit IEEE floating-point number. + + void Put_real32 (real32 x); + + /// Get one 64-bit IEEE floating-point number from stream and advance + /// read position. Byte swap if byte swapping is turned on. + /// \retval One 64-bit IEEE floating-point number . + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + real64 Get_real64 (); + + /// Put one 64-bit IEEE floating-point number to stream and advance write + /// position. Byte swap if byte swapping is turned on. + /// \param x One64-bit IEEE floating-point number. + + void Put_real64 (real64 x); + + /// Get an 8-bit character string from stream and advance read position. + /// Routine always reads until a NUL character (8-bits of zero) is read. + /// (That is, only maxLength bytes will be returned in buffer, but the + /// stream is always advanced until a NUL is read or EOF is reached.) + /// \param data Buffer in which string is returned. + /// \param maxLength Maximum number of bytes to place in buffer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if stream runs out before NUL is seen. + + void Get_CString (char *data, + uint32 maxLength); + + /// Get a 16-bit character string from stream and advance read position. + /// 16-bit characters are truncated to 8-bits. + /// Routine always reads until a NUL character (16-bits of zero) is read. + /// (That is, only maxLength bytes will be returned in buffer, but the + /// stream is always advanced until a NUL is read or EOF is reached.) + /// \param data Buffer to place string in. + /// \param maxLength Maximum number of bytes to place in buffer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if stream runs out before NUL is seen. + + void Get_UString (char *data, + uint32 maxLength); + + /// Writes the specified number of zero bytes to stream. + /// \param count Number of zero bytes to write. + + void PutZeros (uint64 count); + + /// Writes zeros to align the stream position to a multiple of 2. + + void PadAlign2 (); + + /// Writes zeros to align the stream position to a multiple of 4. + + void PadAlign4 (); + + /// Get a value of size indicated by tag type from stream and advance + /// read position. Byte swap if byte swapping is turned on and tag type + /// is larger than a byte. Value is returned as an unsigned 32-bit integer. + /// \param tagType Tag type of data stored in stream. + /// \retval One unsigned 32-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + uint32 TagValue_uint32 (uint32 tagType); + + /// Get a value of size indicated by tag type from stream and advance read + /// position. Byte swap if byte swapping is turned on and tag type is larger + /// than a byte. Value is returned as a 32-bit integer. + /// \param tagType Tag type of data stored in stream. + /// \retval One 32-bit integer. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + int32 TagValue_int32 (uint32 tagType); + + /// Get a value of size indicated by tag type from stream and advance read + /// position. Byte swap if byte swapping is turned on and tag type is larger + /// than a byte. Value is returned as a dng_urational. + /// \param tagType Tag type of data stored in stream. + /// \retval One dng_urational. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + dng_urational TagValue_urational (uint32 tagType); + + /// Get a value of size indicated by tag type from stream and advance read + /// position. Byte swap if byte swapping is turned on and tag type is larger + /// than a byte. Value is returned as a dng_srational. + /// \param tagType Tag type of data stored in stream. + /// \retval One dng_srational. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + dng_srational TagValue_srational (uint32 tagType); + + /// Get a value of size indicated by tag type from stream and advance read + /// position. Byte swap if byte swapping is turned on and tag type is larger + /// than a byte. Value is returned as a 64-bit IEEE floating-point number. + /// \param tagType Tag type of data stored in stream. + /// \retval One 64-bit IEEE floating-point number. + /// \exception dng_exception with fErrorCode equal to dng_error_end_of_file + /// if not enough data in stream. + + real64 TagValue_real64 (uint32 tagType); + + /// Getter for sniffer associated with stream. + /// \retval The sniffer for this stream. + + dng_abort_sniffer * Sniffer () const + { + return fSniffer; + } + + /// Putter for sniffer associated with stream. + /// \param sniffer The new sniffer to use (or NULL for none). + + void SetSniffer (dng_abort_sniffer *sniffer) + { + fSniffer = sniffer; + } + + /// Copy a specified number of bytes to a target stream. + /// \param dstStream The target stream. + /// \param count The number of bytes to copy. + + virtual void CopyToStream (dng_stream &dstStream, + uint64 count); + + /// Makes the target stream a copy of this stream. + /// \param dstStream The target stream. + + void DuplicateStream (dng_stream &dstStream); + + private: + + // Hidden copy constructor and assignment operator. + + dng_stream (const dng_stream &stream); + + dng_stream & operator= (const dng_stream &stream); + + }; + +/*****************************************************************************/ + +class TempBigEndian + { + + private: + + dng_stream & fStream; + + bool fOldSwap; + + public: + + TempBigEndian (dng_stream &stream, + bool bigEndian = true); + + virtual ~TempBigEndian (); + + }; + +/*****************************************************************************/ + +class TempLittleEndian: public TempBigEndian + { + + public: + + TempLittleEndian (dng_stream &stream, + bool littleEndian = true) + + : TempBigEndian (stream, !littleEndian) + + { + } + + virtual ~TempLittleEndian () + { + } + + }; + +/*****************************************************************************/ + +class TempStreamSniffer + { + + private: + + dng_stream & fStream; + + dng_abort_sniffer *fOldSniffer; + + public: + + TempStreamSniffer (dng_stream &stream, + dng_abort_sniffer *sniffer); + + virtual ~TempStreamSniffer (); + + private: + + // Hidden copy constructor and assignment operator. + + TempStreamSniffer (const TempStreamSniffer &temp); + + TempStreamSniffer & operator= (const TempStreamSniffer &temp); + + }; + +/*****************************************************************************/ + +class PreserveStreamReadPosition + { + + private: + + dng_stream & fStream; + + uint64 fPosition; + + public: + + PreserveStreamReadPosition (dng_stream &stream) + + : fStream (stream) + , fPosition (stream.Position ()) + + { + } + + ~PreserveStreamReadPosition () + { + fStream.SetReadPosition (fPosition); + } + + private: + + // Hidden copy constructor and assignment operator. + + PreserveStreamReadPosition (const PreserveStreamReadPosition &rhs); + + PreserveStreamReadPosition & operator= (const PreserveStreamReadPosition &rhs); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_string.cpp b/source/lib/dng_sdk/dng_string.cpp new file mode 100644 index 0000000..2e95f43 --- /dev/null +++ b/source/lib/dng_sdk/dng_string.cpp @@ -0,0 +1,2242 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_string.cpp#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_string.h" + +#include "dng_assertions.h" +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_mutex.h" +#include "dng_utils.h" + +#if qMacOS && qEnableCarbon +#include +#endif + +#if qWinOS +#include +#endif + +#include // for isdigit + +#include "stdc_includes.h" + +/*****************************************************************************/ + +const uint32 kREPLACEMENT_CHARACTER = 0x0000FFFD; + +/*****************************************************************************/ + +#if qMacOS && qEnableCarbon + +static void Assign_Multibyte (dng_string &dngString, + const char *otherString, + TextEncoding encoding) + { + + uint32 aSize = (uint32) strlen (otherString); + + if (aSize > 0) + { + + uint32 aBufSize = aSize * 6 + 256; + + dng_memory_data aBuf (aBufSize + 1); + + UnicodeMapping aMapping; + + aMapping.unicodeEncoding = ::CreateTextEncoding (kTextEncodingUnicodeV3_0, + kUnicodeNoSubset, + kUnicodeUTF8Format); + + aMapping.otherEncoding = encoding; + aMapping.mappingVersion = kUnicodeUseLatestMapping; + + TextToUnicodeInfo aInfo = NULL; + + if (::CreateTextToUnicodeInfo (&aMapping, &aInfo) == noErr) + { + + ByteCount aInput = 0; + ByteCount aOutput = 0; + + ::ConvertFromTextToUnicode (aInfo, + aSize, + otherString, + kUnicodeUseFallbacksMask | + kUnicodeLooseMappingsMask, + 0, + NULL, + NULL, + NULL, + aBufSize, + &aInput, + &aOutput, + (UniChar *) aBuf.Buffer ()); + + ::DisposeTextToUnicodeInfo (&aInfo); + + if (aOutput > 0 && aOutput <= aBufSize) + { + + char *aBufChar = aBuf.Buffer_char (); + + aBufChar [aOutput] = 0; + + dngString.Set (aBufChar); + + return; + + } + + } + + } + + dngString.Clear (); + + } + +static uint32 Extract_Multibyte (const dng_string &dngString, + dng_memory_data &buffer, + TextEncoding encoding) + { + + uint32 aSize = dngString.Length (); + + if (aSize > 0) + { + + uint32 aBufSize = (aSize * 2) + 256; + + dng_memory_data tempBuffer (aBufSize); + + UnicodeMapping aMapping; + + aMapping.unicodeEncoding = ::CreateTextEncoding (kTextEncodingUnicodeV3_0, + kUnicodeNoSubset, + kUnicodeUTF8Format); + + aMapping.otherEncoding = encoding; + aMapping.mappingVersion = kUnicodeUseLatestMapping; + + UnicodeToTextInfo aInfo = NULL; + + if (::CreateUnicodeToTextInfo (&aMapping, &aInfo) == noErr) + { + + ByteCount aInput = 0; + ByteCount aOutput = 0; + + ::ConvertFromUnicodeToText (aInfo, + aSize, + (const UniChar *) dngString.Get (), + kUnicodeUseFallbacksMask | + kUnicodeLooseMappingsMask | + kUnicodeDefaultDirectionMask, + 0, + NULL, + NULL, + NULL, + aBufSize, + &aInput, + &aOutput, + tempBuffer.Buffer_char ()); + + ::DisposeUnicodeToTextInfo (&aInfo); + + if (aOutput > 0) + { + + buffer.Allocate ((uint32) (aOutput + 1)); + + memcpy (buffer.Buffer (), + tempBuffer.Buffer (), + aOutput); + + buffer.Buffer_char () [aOutput] = 0; + + return (uint32) aOutput; + + } + + } + + } + + buffer.Allocate (1); + + buffer.Buffer_char () [0] = 0; + + return 0; + + } + +static void Assign_SystemEncoding (dng_string &dngString, + const char *otherString) + { + + TextEncoding aEncoding; + + ::UpgradeScriptInfoToTextEncoding (smSystemScript, + kTextLanguageDontCare, + kTextRegionDontCare, + NULL, + &aEncoding); + + Assign_Multibyte (dngString, + otherString, + aEncoding); + + } + +static uint32 Extract_SystemEncoding (const dng_string &dngString, + dng_memory_data &buffer) + { + + TextEncoding aEncoding; + + ::UpgradeScriptInfoToTextEncoding (smSystemScript, + kTextLanguageDontCare, + kTextRegionDontCare, + NULL, + &aEncoding); + + return Extract_Multibyte (dngString, + buffer, + aEncoding); + + } + +static void Assign_JIS_X208_1990 (dng_string &dngString, + const char *otherString) + { + + Assign_Multibyte (dngString, + otherString, + kTextEncodingJIS_X0208_90); + + } + +#endif + +/*****************************************************************************/ + +#if qWinOS + +static void Assign_Multibyte (dng_string &dngString, + const char *otherString, + UINT encoding) + { + + DNG_ASSERT (sizeof (WCHAR) == 2, "WCHAR must be 2 bytes"); + + int aSize = (int) strlen (otherString); + + if (aSize > 0) + { + + int aBufChars = aSize * 3 + 128; + + dng_memory_data aBuf ((aBufChars + 1) << 1); + + int aResult = ::MultiByteToWideChar (encoding, + 0, + otherString, + aSize, + (WCHAR *) aBuf.Buffer (), + aBufChars); + + if (aResult > 0 && aResult <= aBufChars) + { + + uint16 * aUTF16 = aBuf.Buffer_uint16 (); + + aUTF16 [aResult] = 0; + + dngString.Set_UTF16 (aUTF16); + + return; + + } + + } + + dngString.Clear (); + + } + +static uint32 Extract_Multibyte (const dng_string &dngString, + dng_memory_data &buffer, + UINT encoding) + { + + DNG_ASSERT (sizeof (WCHAR) == 2, "WCHAR must be 2 bytes"); + + dng_memory_data sBuffer; + + int aCount = dngString.Get_UTF16 (sBuffer); + + int dBufSize = aCount * 2 + 256; + + dng_memory_data dBuffer (dBufSize); + + int aResult = ::WideCharToMultiByte (encoding, + 0, + (WCHAR *) sBuffer.Buffer (), + aCount, + dBuffer.Buffer_char (), + dBufSize, + NULL, + NULL); + + if (aResult < 0) + aResult = 0; + + buffer.Allocate (aResult + 1); + + memcpy (buffer.Buffer (), + dBuffer.Buffer (), + aResult); + + buffer.Buffer_char () [aResult] = 0; + + return (uint32) aResult; + + } + +static void Assign_SystemEncoding (dng_string &dngString, + const char *otherString) + { + + Assign_Multibyte (dngString, + otherString, + ::GetACP ()); + + } + +static uint32 Extract_SystemEncoding (const dng_string &dngString, + dng_memory_data &buffer) + { + + return Extract_Multibyte (dngString, + buffer, + ::GetACP ()); + + } + +static void Assign_JIS_X208_1990 (dng_string &dngString, + const char *otherString) + { + + // From MSDN documentation: 20932 = JIS X 0208-1990 & 0121-1990 + + const UINT kJIS = 20932; + + Assign_Multibyte (dngString, + otherString, + kJIS); + + } + +#endif + +/*****************************************************************************/ + +static bool IsASCII (const char *s) + { + + if (!s) + { + + return true; + + } + + while (true) + { + + uint8 c = (uint8) *(s++); + + if (c == 0) + { + + break; + + } + + if (c & 0x80) + { + + return false; + + } + + } + + return true; + + } + +/*****************************************************************************/ + +dng_string::dng_string () + + : fData () + + { + + } + +/*****************************************************************************/ + +dng_string::dng_string (const dng_string &s) + + : fData () + + { + + Set (s.Get ()); + + } + +/*****************************************************************************/ + +dng_string & dng_string::operator= (const dng_string &s) + { + + if (this != &s) + { + + Set (s.Get ()); + + } + + return *this; + + } + +/*****************************************************************************/ + +dng_string::~dng_string () + { + + } + +/*****************************************************************************/ + +const char * dng_string::Get () const + { + + if (fData.Buffer ()) + { + + return fData.Buffer_char (); + + } + + return ""; + + } + +/*****************************************************************************/ + +bool dng_string::IsASCII () const + { + + return ::IsASCII (Get ()); + + } + +/*****************************************************************************/ + +void dng_string::Set (const char *s) + { + + // Measure the new length. + + uint32 newLen = (s != NULL ? (uint32) strlen (s) : 0); + + // If it is a NULL string, then clear the buffer. + + if (newLen == 0) + { + + fData.Clear (); + + } + + // Else we need to copy the bytes. + + else + { + + uint32 oldLen = Length (); + + // We might be setting this string to a sub-string of itself, + // so don't reallocate the data unless the string is getting + // longer. + + if (newLen > oldLen) + { + + fData.Clear (); + + fData.Allocate (newLen + 1); + + } + + char *d = fData.Buffer_char (); + + for (uint32 k = 0; k <= newLen; k++) + { + + d [k] = s [k]; + + } + + } + + } + +/*****************************************************************************/ + +void dng_string::Set_ASCII (const char *s) + { + + if (::IsASCII (s)) + { + + Set (s); + + } + + else + { + + Set_SystemEncoding (s); + + } + + } + +/*****************************************************************************/ + +void dng_string::Set_UTF8 (const char *s) + { + + uint32 len = (uint32) strlen (s); + + const char *sEnd = s + len; + + // Worst case expansion is 1-byte characters expanding to + // replacement character, which requires 3 bytes. + + dng_memory_data buffer (len * 3 + 1); + + uint8 *d = buffer.Buffer_uint8 (); + + while (s < sEnd) + { + + uint32 aChar = DecodeUTF8 (s, (uint32) (sEnd - s)); + + if (aChar > 0x7FFFFFFF) + { + aChar = kREPLACEMENT_CHARACTER; + } + + #if qDNGValidate + + if (aChar == kREPLACEMENT_CHARACTER) + { + ReportWarning ("Expected UTF-8 value is not valid UTF-8 (or contains a kREPLACEMENT_CHARACTER)"); + } + + #endif + + if (aChar < 0x00000080) + { + *(d++) = (uint8) aChar; + } + + else if (aChar < 0x00000800) + { + *(d++) = (uint8) ((aChar >> 6) | 0x000000C0); + *(d++) = (uint8) ((aChar & 0x0000003F) | 0x00000080); + } + + else if (aChar < 0x00010000) + { + *(d++) = (uint8) ( (aChar >> 12) | 0x000000E0); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + else if (aChar < 0x00200000) + { + *(d++) = (uint8) ( (aChar >> 18) | 0x000000F0); + *(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + else if (aChar < 0x04000000) + { + *(d++) = (uint8) ( (aChar >> 24) | 0x000000F8); + *(d++) = (uint8) (((aChar >> 18) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + else + { + *(d++) = (uint8) ( (aChar >> 30) | 0x000000FC); + *(d++) = (uint8) (((aChar >> 24) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 18) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + } + + *d = 0; + + Set (buffer.Buffer_char ()); + + } + +/*****************************************************************************/ + +uint32 dng_string::Get_SystemEncoding (dng_memory_data &buffer) const + { + + if (IsASCII ()) + { + + uint32 len = Length (); + + buffer.Allocate (len + 1); + + memcpy (buffer.Buffer (), Get (), len + 1); + + return len; + + } + + else + { + + #if (qMacOS && qEnableCarbon) || qWinOS + + return Extract_SystemEncoding (*this, buffer); + + #else + + // Fallback logic to force the string to ASCII. + + dng_string temp (*this); + + temp.ForceASCII (); + + return temp.Get_SystemEncoding (buffer); + + #endif + + } + + } + +/*****************************************************************************/ + +void dng_string::Set_SystemEncoding (const char *s) + { + + if (::IsASCII (s)) + { + + Set (s); + + } + + else + { + + #if (qMacOS && qEnableCarbon) || qWinOS + + Assign_SystemEncoding (*this, s); + + #else + + // Fallback logic that just grabs the ASCII characters and + // ignores the non-ASCII characters. + + uint32 len = (uint32) strlen (s); + + dng_memory_data buffer (len + 1); + + uint8 *d = buffer.Buffer_uint8 (); + + while (*s) + { + + uint8 c = (uint8) *(s++); + + if ((c & 0x80) == 0) + { + + *(d++) = c; + + } + + } + + *d = 0; + + Set (buffer.Buffer_char ()); + + #endif + + } + + } + +/*****************************************************************************/ + +bool dng_string::ValidSystemEncoding () const + { + + if (IsASCII ()) + { + + return true; + + } + + dng_memory_data buffer; + + Get_SystemEncoding (buffer); + + dng_string temp; + + temp.Set_SystemEncoding (buffer.Buffer_char ()); + + return (*this == temp); + + } + +/*****************************************************************************/ + +void dng_string::Set_JIS_X208_1990 (const char *s) + { + + if (::IsASCII (s)) + { + + Set (s); + + } + + else + { + + #if (qMacOS && qEnableCarbon) || qWinOS + + Assign_JIS_X208_1990 (*this, s); + + #else + + // Fallback to the ASCII extraction logic. + + Set_SystemEncoding (s); + + #endif + + } + + } + +/*****************************************************************************/ + +uint32 dng_string::DecodeUTF8 (const char *&s, + uint32 maxBytes, + bool *isValid) + { + + static const uint8 gUTF8Bytes [256] = + { + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, + 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, + 3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,6,6,6,6 + }; + + if (isValid) + { + *isValid = true; + } + + const uint8 *nBuf = (const uint8 *) s; + + uint32 aChar = nBuf [0]; + + uint32 aSize = gUTF8Bytes [aChar]; + + if (aSize > maxBytes) + { + + s += maxBytes; + + if (isValid) + { + *isValid = false; + } + + return kREPLACEMENT_CHARACTER; + + } + + s += aSize; + + for (uint32 extra = 1; extra < aSize; extra++) + { + + if ((nBuf [extra] & 0xC0) != 0x80) + { + + if (isValid) + { + *isValid = false; + } + + return kREPLACEMENT_CHARACTER; + + } + + } + + switch (aSize) + { + + case 0: + { + + s++; // Don't get stuck in infinite loop + + if (isValid) + { + *isValid = false; + } + + return kREPLACEMENT_CHARACTER; + + } + + case 1: + { + + return aChar; + + } + + case 2: + { + + aChar = ((aChar << 6) + nBuf [1]) - (uint32) 0x00003080UL; + + break; + + } + + case 3: + { + + aChar = ((((aChar << 6) + nBuf [1]) + << 6) + nBuf [2]) - (uint32) 0x000E2080UL; + + break; + + } + + case 4: + { + + aChar = ((((((aChar << 6) + nBuf [1]) + << 6) + nBuf [2]) + << 6) + nBuf [3]) - (uint32) 0x03C82080UL; + + break; + + } + + case 5: + { + + aChar = ((((((((aChar << 6) + nBuf [1]) + << 6) + nBuf [2]) + << 6) + nBuf [3]) + << 6) + nBuf [4]) - (uint32) 0xFA082080UL; + + break; + + } + + case 6: + { + + aChar = ((((((((((aChar << 6) + nBuf [1]) + << 6) + nBuf [2]) + << 6) + nBuf [3]) + << 6) + nBuf [4]) + << 6) + nBuf [5]) - (uint32) 0x82082080UL; + + break; + + } + + } + + if (aChar < 0x7F || aChar > 0x0010FFFF) + { + + if (isValid) + { + *isValid = false; + } + + return kREPLACEMENT_CHARACTER; + + } + + return aChar; + + } + +/*****************************************************************************/ + +bool dng_string::IsUTF8 (const char *s) + { + + uint32 len = (uint32) strlen (s); + + const char *sEnd = s + len; + + while (s < sEnd) + { + + bool isValid = true; + + (void) DecodeUTF8 (s, (uint32) (sEnd - s), &isValid); + + if (!isValid) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +void dng_string::Set_UTF8_or_System (const char *s) + { + + if (::IsASCII (s)) + { + + Set (s); + + } + + else if (IsUTF8 (s)) + { + + Set_UTF8 (s); + + } + + else + { + + Set_SystemEncoding (s); + + } + + } + +/*****************************************************************************/ + +uint32 dng_string::Get_UTF16 (dng_memory_data &buffer) const + { + + uint32 count = 0; + + const char *sPtr = Get (); + + while (*sPtr) + { + + uint32 x = DecodeUTF8 (sPtr); + + if (x <= 0x0000FFFF || + x > 0x0010FFFF) + { + + count += 1; + + } + + else + { + + count += 2; + + } + + } + + buffer.Allocate ((count + 1) * (uint32) sizeof (uint16)); + + uint16 *dPtr = buffer.Buffer_uint16 (); + + sPtr = Get (); + + while (*sPtr) + { + + uint32 x = DecodeUTF8 (sPtr); + + if (x <= 0x0000FFFF) + { + + *(dPtr++) = (uint16) x; + + } + + else if (x > 0x0010FFFF) + { + + *(dPtr++) = (uint16) kREPLACEMENT_CHARACTER; + + } + + else + { + + x -= 0x00010000; + + *(dPtr++) = (uint16) ((x >> 10 ) + 0x0000D800); + *(dPtr++) = (uint16) ((x & 0x000003FF) + 0x0000DC00); + + } + + } + + *dPtr = 0; + + return count; + + } + +/*****************************************************************************/ + +void dng_string::Set_UTF16 (const uint16 *s) + { + + if (!s) + { + Clear (); + return; + } + + bool swap = false; + + if (s [0] == 0xFFFE) // Swapped byte order marker + { + swap = true; + s++; + } + + else if (s [0] == 0xFEFF) // Non-swapped byte order marker + { + s++; + } + + uint32 length16 = 0; + + while (s [length16] != 0) + { + length16++; + } + + const uint16 *sEnd = s + length16; + + dng_memory_data buffer (length16 * 6 + 1); + + uint8 *d = buffer.Buffer_uint8 (); + + while (s < sEnd) + { + + uint32 aChar = *s++; + + if (swap) + { + aChar = ((aChar << 8) | (aChar >> 8)) & 0x0000FFFF; + } + + if ((aChar >= 0x0000D800) && (aChar <= 0x0000DBFF) && (s < sEnd)) + { + + uint32 aLow = *s; + + if (swap) + { + aLow = ((aLow << 8) | (aLow >> 8)) & 0x0000FFFF; + } + + if ((aLow >= 0x0000DC00) && (aLow <= 0x0000DFFF)) + { + + aChar = ((aChar - 0x0000D800) << 10) + + (aLow - 0x0000DC00) + + 0x00010000; + + s++; + + } + + } + + if (aChar > 0x7FFFFFFF) + { + aChar = kREPLACEMENT_CHARACTER; + } + + if (aChar < 0x00000080) + { + *(d++) = (uint8) aChar; + } + + else if (aChar < 0x00000800) + { + *(d++) = (uint8) ((aChar >> 6) | 0x000000C0); + *(d++) = (uint8) ((aChar & 0x0000003F) | 0x00000080); + } + + else if (aChar < 0x00010000) + { + *(d++) = (uint8) ( (aChar >> 12) | 0x000000E0); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + else if (aChar < 0x00200000) + { + *(d++) = (uint8) ( (aChar >> 18) | 0x000000F0); + *(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + else if (aChar < 0x04000000) + { + *(d++) = (uint8) ( (aChar >> 24) | 0x000000F8); + *(d++) = (uint8) (((aChar >> 18) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + else + { + *(d++) = (uint8) ( (aChar >> 30) | 0x000000FC); + *(d++) = (uint8) (((aChar >> 24) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 18) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 12) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) (((aChar >> 6) & 0x0000003F) | 0x00000080); + *(d++) = (uint8) ( (aChar & 0x0000003F) | 0x00000080); + } + + } + + *d = 0; + + Set (buffer.Buffer_char ()); + + } + +/*****************************************************************************/ + +void dng_string::Clear () + { + + Set (NULL); + + } + +/*****************************************************************************/ + +void dng_string::Truncate (uint32 maxBytes) + { + + uint32 len = Length (); + + if (len > maxBytes) + { + + uint8 *s = fData.Buffer_uint8 (); + + // Don't truncate on an extension character. Extensions characters + // in UTF-8 have the 0x80 bit set and the 0x40 bit clear. + + while (maxBytes > 0 && ((s [maxBytes]) & 0xC0) == 0x80) + { + + maxBytes--; + + } + + s [maxBytes] = 0; + + } + + } + +/*****************************************************************************/ + +bool dng_string::TrimTrailingBlanks () + { + + bool didTrim = false; + + if (fData.Buffer ()) + { + + char *s = fData.Buffer_char (); + + uint32 len = (uint32) strlen (s); + + while (len > 0 && s [len - 1] == ' ') + { + len--; + didTrim = true; + } + + s [len] = 0; + + } + + return didTrim; + + } + +/*****************************************************************************/ + +bool dng_string::TrimLeadingBlanks () + { + + bool didTrim = false; + + const char *s = Get (); + + while (*s == ' ') + { + s++; + didTrim = true; + } + + if (didTrim) + { + Set (s); + } + + return didTrim; + + } + +/*****************************************************************************/ + +bool dng_string::IsEmpty () const + { + + const char *s = Get (); + + return *s == 0; + + } + +/*****************************************************************************/ + +uint32 dng_string::Length () const + { + + const char *s = Get (); + + return (uint32) strlen (s); + + } + +/*****************************************************************************/ + +bool dng_string::operator== (const dng_string &s) const + { + + const char *s1 = Get (); + const char *s2 = s.Get (); + + return strcmp (s1, s2) == 0; + + } + +/*****************************************************************************/ + +bool dng_string::Matches (const char *t, + const char *s, + bool case_sensitive) + { + + while (*s != 0) + { + + char c1 = *(s++); + char c2 = *(t++); + + if (!case_sensitive) + { + c1 = ForceUppercase (c1); + c2 = ForceUppercase (c2); + } + + if (c1 != c2) + { + return false; + } + + } + + return (*t == 0); + + } + +/*****************************************************************************/ + +bool dng_string::Matches (const char *s, + bool case_sensitive) const + { + + return dng_string::Matches (Get (), s, case_sensitive); + + } + +/*****************************************************************************/ + +bool dng_string::StartsWith (const char *s, + bool case_sensitive) const + { + + const char *t = Get (); + + while (*s != 0) + { + + char c1 = *(s++); + char c2 = *(t++); + + if (!case_sensitive) + { + c1 = ForceUppercase (c1); + c2 = ForceUppercase (c2); + } + + if (c1 != c2) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_string::EndsWith (const char *s, + bool case_sensitive) const + { + + uint32 len1 = Length (); + + uint32 len2 = (uint32) strlen (s); + + if (len1 < len2) + { + return false; + } + + const char *t = Get () + (len1 - len2); + + while (*s != 0) + { + + char c1 = *(s++); + char c2 = *(t++); + + if (!case_sensitive) + { + c1 = ForceUppercase (c1); + c2 = ForceUppercase (c2); + } + + if (c1 != c2) + { + return false; + } + + } + + return true; + + } + +/*****************************************************************************/ + +bool dng_string::Contains (const char *s, + bool case_sensitive, + int32 *match_offset) const + { + + if (match_offset) + { + *match_offset = -1; + } + + uint32 len1 = Length (); + + uint32 len2 = (uint32) strlen (s); + + if (len1 < len2) + { + return false; + } + + uint32 offsets = len1 - len2; + + for (uint32 offset = 0; offset <= offsets; offset++) + { + + const char *ss = s; + const char *tt = Get () + offset; + + while (*ss != 0) + { + + char c1 = *(ss++); + char c2 = *(tt++); + + if (!case_sensitive) + { + c1 = ForceUppercase (c1); + c2 = ForceUppercase (c2); + } + + if (c1 != c2) + { + goto tryNextOffset; + } + + } + + if (match_offset) + { + *match_offset = offset; + } + + return true; + + tryNextOffset: ; + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_string::Replace (const char *old_string, + const char *new_string, + bool case_sensitive) + { + + int32 match_offset = -1; + + if (Contains (old_string, + case_sensitive, + &match_offset)) + { + + uint32 len1 = Length (); + + uint32 len2 = (uint32) strlen (old_string); + uint32 len3 = (uint32) strlen (new_string); + + if (len2 == len3) + { + + strncpy (fData.Buffer_char () + match_offset, + new_string, + len3); + + } + + else if (len2 > len3) + { + + strncpy (fData.Buffer_char () + match_offset, + new_string, + len3); + + const char *s = fData.Buffer_char () + match_offset + len2; + char *d = fData.Buffer_char () + match_offset + len3; + + uint32 extra = len1 - match_offset - len2 + 1; // + 1 for NULL termination + + for (uint32 j = 0; j < extra; j++) + { + *(d++) = *(s++); + } + + } + + else + { + + dng_memory_data tempBuffer (len1 - len2 + len3 + 1); + + if (match_offset) + { + + strncpy (tempBuffer.Buffer_char (), + fData .Buffer_char (), + match_offset); + + } + + if (len3) + { + + strncpy (tempBuffer.Buffer_char () + match_offset, + new_string, + len3); + + } + + uint32 extra = len1 - match_offset - len2 + 1; // + 1 for NULL termination + + strncpy (tempBuffer.Buffer_char () + match_offset + len3, + fData .Buffer_char () + match_offset + len2, + extra); + + Set (tempBuffer.Buffer_char ()); + + } + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_string::TrimLeading (const char *s, + bool case_sensitive) + { + + if (StartsWith (s, case_sensitive)) + { + + Set (Get () + (uint32) strlen (s)); + + return true; + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_string::Append (const char *s) + { + + uint32 len2 = (uint32) strlen (s); + + if (len2) + { + + uint32 len1 = Length (); + + dng_memory_data temp (len1 + len2 + 1); + + char *buffer = temp.Buffer_char (); + + if (len1) + { + memcpy (buffer, Get (), len1); + } + + memcpy (buffer + len1, s, len2 + 1); + + Set (buffer); + + } + + } + +/*****************************************************************************/ + +void dng_string::SetUppercase () + { + + if (fData.Buffer ()) + { + + uint32 len = Length (); + + char *dPtr = fData.Buffer_char (); + + for (uint32 j = 0; j < len; j++) + { + + char c = dPtr [j]; + + if (c >= 'a' && c <= 'z') + { + + dPtr [j] = c - 'a' + 'A'; + + } + + } + + } + + } + +/*****************************************************************************/ + +void dng_string::SetLowercase () + { + + if (fData.Buffer ()) + { + + uint32 len = Length (); + + char *dPtr = fData.Buffer_char (); + + for (uint32 j = 0; j < len; j++) + { + + char c = dPtr [j]; + + if (c >= 'A' && c <= 'Z') + { + + dPtr [j] = c - 'A' + 'a'; + + } + + } + + } + + } + +/*****************************************************************************/ + +void dng_string::SetLineEndings (char ending) + { + + if (fData.Buffer ()) + { + + const char *sPtr = fData.Buffer_char (); + char *dPtr = fData.Buffer_char (); + + while (*sPtr) + { + + char c = *(sPtr++); + + char nc = sPtr [0]; + + if ((c == '\r' && nc == '\n') || + (c == '\n' && nc == '\r')) + { + + sPtr++; + + if (ending) + { + *(dPtr++) = ending; + } + + } + + else if (c == '\n' || + c == '\r') + { + + if (ending) + { + *(dPtr++) = ending; + } + + } + + else + { + + *(dPtr++) = c; + + } + + } + + *dPtr = 0; + + } + + } + +/*****************************************************************************/ + +void dng_string::StripLowASCII () + { + + if (fData.Buffer ()) + { + + const char *sPtr = fData.Buffer_char (); + char *dPtr = fData.Buffer_char (); + + while (*sPtr) + { + + char c = *(sPtr++); + + if (c == '\r' || c == '\n' || (uint8) c >= ' ') + { + + *(dPtr++) = c; + + } + + } + + *dPtr = 0; + + } + + } + +/*****************************************************************************/ + +void dng_string::NormalizeAsCommaSeparatedNumbers () + { + + if (fData.Buffer ()) + { + + const char *sPtr = fData.Buffer_char (); + char *dPtr = fData.Buffer_char (); + + bool commaInserted = false; + + while (*sPtr) + { + + uint32 c = DecodeUTF8 (sPtr); + + // Support number formats such as "3", "+3.0", "-3.1416", "314.16e-2", + // "0.31416E1", but no hex/octal number representations. + + if (isdigit ((int) c) || c == '.' || c == '-' || c == '+' || c == 'e' || c == 'E') + { + + *(dPtr++) = (char) c; + + if (commaInserted) + { + + commaInserted = false; + + } + + } + + else if (!commaInserted) + { + + *(dPtr++) = ','; + + commaInserted = true; + + } + + } + + *dPtr = 0; + + } + + } + +/******************************************************************************/ + +// Unicode to low-ASCII strings table. + +struct UnicodeToLowASCIIEntry + { + uint32 unicode; + const char *ascii; + }; + +static const UnicodeToLowASCIIEntry kUnicodeToLowASCII [] = + { + { 0x00A0, " " }, + { 0x00A1, "!" }, + { 0x00A9, "(C)" }, + { 0x00AA, "a" }, + { 0x00AB, "<<" }, + { 0x00AC, "!" }, + { 0x00AE, "(R)" }, + { 0x00B0, "dg" }, + { 0x00B1, "+-" }, + { 0x00B7, "." }, + { 0x00BA, "o" }, + { 0x00BB, ">>" }, + { 0x00BF, "?" }, + { 0x00C0, "A" }, + { 0x00C1, "A" }, + { 0x00C2, "A" }, + { 0x00C3, "A" }, + { 0x00C4, "A" }, + { 0x00C5, "A" }, + { 0x00C6, "AE" }, + { 0x00C7, "C" }, + { 0x00C8, "E" }, + { 0x00C9, "E" }, + { 0x00CA, "E" }, + { 0x00CB, "E" }, + { 0x00CC, "I" }, + { 0x00CD, "I" }, + { 0x00CE, "I" }, + { 0x00CF, "I" }, + { 0x00D1, "N" }, + { 0x00D2, "O" }, + { 0x00D3, "O" }, + { 0x00D4, "O" }, + { 0x00D5, "O" }, + { 0x00D6, "O" }, + { 0x00D8, "O" }, + { 0x00D9, "U" }, + { 0x00DA, "U" }, + { 0x00DB, "U" }, + { 0x00DC, "U" }, + { 0x00DD, "Y" }, + { 0x00E0, "a" }, + { 0x00E1, "a" }, + { 0x00E2, "a" }, + { 0x00E3, "a" }, + { 0x00E4, "a" }, + { 0x00E5, "a" }, + { 0x00E6, "ae" }, + { 0x00E7, "c" }, + { 0x00E8, "e" }, + { 0x00E9, "e" }, + { 0x00EA, "e" }, + { 0x00EB, "e" }, + { 0x00EC, "i" }, + { 0x00ED, "i" }, + { 0x00EE, "i" }, + { 0x00EF, "i" }, + { 0x00F1, "n" }, + { 0x00F2, "o" }, + { 0x00F3, "o" }, + { 0x00F4, "o" }, + { 0x00F5, "o" }, + { 0x00F6, "o" }, + { 0x00F7, "/" }, + { 0x00F8, "o" }, + { 0x00F9, "u" }, + { 0x00FA, "u" }, + { 0x00FB, "u" }, + { 0x00FC, "u" }, + { 0x00FD, "y" }, + { 0x00FF, "y" }, + { 0x0131, "i" }, + { 0x0152, "OE" }, + { 0x0153, "oe" }, + { 0x0178, "Y" }, + { 0x2013, "-" }, + { 0x2014, "-" }, + { 0x2018, "'" }, + { 0x2019, "'" }, + { 0x201A, "," }, + { 0x201C, "\"" }, + { 0x201D, "\"" }, + { 0x201E, ",," }, + { 0x2022, "." }, + { 0x2026, "..." }, + { 0x2039, "<" }, + { 0x203A, ">" }, + { 0x2044, "/" }, + { 0x2122, "TM" }, + { 0x2206, "d" }, + { 0x2211, "S" }, + { 0x2260, "!=" }, + { 0x2264, "<=" }, + { 0x2265, ">=" }, + { 0x2318, "#" }, + { 0xFB01, "fi" }, + { 0xFB02, "fl" } + }; + +/******************************************************************************/ + +void dng_string::ForceASCII () + { + + if (!IsASCII ()) + { + + dng_memory_data tempBuffer (Length () * 3 + 1); + + char *dPtr = tempBuffer.Buffer_char (); + + const char *sPtr = Get (); + + while (*sPtr) + { + + uint32 x = DecodeUTF8 (sPtr); + + if (x <= 0x007F) + { + + *(dPtr++) = (char) x; + + } + + else + { + + const char *ascii = NULL; + + const uint32 kTableEntrys = sizeof (kUnicodeToLowASCII ) / + sizeof (kUnicodeToLowASCII [0]); + + for (uint32 entry = 0; entry < kTableEntrys; entry++) + { + + if (kUnicodeToLowASCII [entry] . unicode == x) + { + + ascii = kUnicodeToLowASCII [entry] . ascii; + + break; + + } + + } + + if (ascii) + { + + while (*ascii) + { + + *(dPtr++) = *(ascii++); + + } + + } + + else + { + + *(dPtr++) ='?'; + + } + + } + + } + + *dPtr = 0; + + Set (tempBuffer.Buffer_char ()); + + } + + } + +/******************************************************************************/ + +static dng_mutex gProtectUCCalls ("gProtectUCCalls"); + +/******************************************************************************/ + +int32 dng_string::Compare (const dng_string &s) const + { + + #if qMacOS && qEnableCarbon + + { + + dng_memory_data aStrA; + dng_memory_data aStrB; + + uint32 aLenA = this->Get_UTF16 (aStrA); + uint32 aLenB = s .Get_UTF16 (aStrB); + + if (aLenA > 0) + { + + if (aLenB > 0) + { + + // For some Mac OS versions anyway, UCCompareTextDefault is not + // thread safe. + + dng_lock_mutex lockMutex (&gProtectUCCalls); + + UCCollateOptions aOptions = kUCCollateStandardOptions | + kUCCollatePunctuationSignificantMask; + + SInt32 aOrder = -1; + + Boolean aEqual = false; + + OSStatus searchStatus = ::UCCompareTextDefault (aOptions, + aStrA.Buffer_uint16 (), + aLenA, + aStrB.Buffer_uint16 (), + aLenB, + &aEqual, + &aOrder); + + if (searchStatus == noErr) + { + + if (aEqual || (aOrder == 0)) + { + return 0; + } + + else + { + return (aOrder > 0) ? 1 : -1; + } + + } + + else + { + + DNG_REPORT ("UCCompareTextDefault failed"); + + return -1; + + } + + } + + else + { + return 1; + } + + } + + else + { + + if (aLenB > 0) + { + return -1; + } + + else + { + return 0; + } + + } + + } + + #elif qWinOS + + { + + dng_memory_data aStrA; + dng_memory_data aStrB; + + uint32 aLenA = this->Get_UTF16 (aStrA); + uint32 aLenB = s .Get_UTF16 (aStrB); + + if (aLenA > 0) + { + + if (aLenB > 0) + { + + LCID locale = LOCALE_SYSTEM_DEFAULT; + + DWORD aFlags = NORM_IGNOREWIDTH; + + int aOrder = ::CompareStringW (locale, + aFlags, + (const WCHAR *) aStrA.Buffer_uint16 (), + aLenA, + (const WCHAR *) aStrB.Buffer_uint16 (), + aLenB); + + if (aOrder == CSTR_EQUAL) + { + return 0; + } + + else if (aOrder == CSTR_GREATER_THAN) + { + return 1; + } + + else + { + return -1; + } + + } + + else + { + return 1; + } + + } + + else + { + + if (aLenB > 0) + { + return -1; + } + else + { + return 0; + } + + } + + } + + #else + + // Fallback to a pure Unicode sort order. + + { + + for (uint32 pass = 0; pass < 2; pass++) + { + + const char *aPtr = Get (); + const char *bPtr = s.Get (); + + while (*aPtr || *bPtr) + { + + if (!bPtr) + { + return 1; + } + + else if (!aPtr) + { + return -1; + } + + uint32 a = DecodeUTF8 (aPtr); + uint32 b = DecodeUTF8 (bPtr); + + // Ignore case on first compare pass. + + if (pass == 0) + { + + if (a >= (uint32) 'a' && a <= (uint32) 'z') + { + a = a - (uint32) 'a' + (uint32) 'A'; + } + + if (b >= (uint32) 'a' && b <= (uint32) 'z') + { + b = b - (uint32) 'a' + (uint32) 'A'; + } + + } + + if (b > a) + { + return 1; + } + + else if (a < b) + { + return -1; + } + + } + + } + + } + + #endif + + return 0; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_string.h b/source/lib/dng_sdk/dng_string.h new file mode 100644 index 0000000..41608f1 --- /dev/null +++ b/source/lib/dng_sdk/dng_string.h @@ -0,0 +1,165 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_string.h#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * Text string representation. + */ + +/*****************************************************************************/ + +#ifndef __dng_string__ +#define __dng_string__ + +/*****************************************************************************/ + +#include "dng_types.h" +#include "dng_memory.h" + +/*****************************************************************************/ + +class dng_string + { + + private: + + // Always stored internally as a UTF-8 encoded string. + + dng_memory_data fData; + + public: + + dng_string (); + + dng_string (const dng_string &s); + + dng_string & operator= (const dng_string &s); + + ~dng_string (); + + const char * Get () const; + + bool IsASCII () const; + + void Set (const char *s); + + void Set_ASCII (const char *s); + + void Set_UTF8 (const char *s); + + uint32 Get_SystemEncoding (dng_memory_data &buffer) const; + + void Set_SystemEncoding (const char *s); + + bool ValidSystemEncoding () const; + + void Set_JIS_X208_1990 (const char *s); + + static uint32 DecodeUTF8 (const char *&s, + uint32 maxBytes = 6, + bool *isValid = NULL); + + static bool IsUTF8 (const char *s); + + void Set_UTF8_or_System (const char *s); + + uint32 Get_UTF16 (dng_memory_data &buffer) const; + + void Set_UTF16 (const uint16 *s); + + void Clear (); + + void Truncate (uint32 maxBytes); + + bool TrimTrailingBlanks (); + + bool TrimLeadingBlanks (); + + bool IsEmpty () const; + + bool NotEmpty () const + { + return !IsEmpty (); + } + + uint32 Length () const; + + bool operator== (const dng_string &s) const; + + bool operator!= (const dng_string &s) const + { + return !(*this == s); + } + + // A utility for doing case insensitive comparisons on strings... + + static bool Matches (const char *t, + const char *s, + bool case_sensitive = false); + + // ...wrapped up for use with dng_string. + + bool Matches (const char *s, + bool case_sensitive = false) const; + + bool StartsWith (const char *s, + bool case_sensitive = false) const; + + bool EndsWith (const char *s, + bool case_sensitive = false) const; + + bool Contains (const char *s, + bool case_sensitive = false, + int32 *match_offset = NULL) const; + + bool Replace (const char *old_string, + const char *new_string, + bool case_sensitive = true); + + bool TrimLeading (const char *s, + bool case_sensitive = false); + + void Append (const char *s); + + void SetUppercase (); + + void SetLowercase (); + + void SetLineEndings (char ending); + + void SetLineEndingsToNewLines () + { + SetLineEndings ('\n'); + } + + void SetLineEndingsToReturns () + { + SetLineEndings ('\r'); + } + + void StripLowASCII (); + + void ForceASCII (); + + int32 Compare (const dng_string &s) const; + + // A utility to convert fields of numbers into comma separated numbers. + + void NormalizeAsCommaSeparatedNumbers (); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_string_list.cpp b/source/lib/dng_sdk/dng_string_list.cpp new file mode 100644 index 0000000..25595f4 --- /dev/null +++ b/source/lib/dng_sdk/dng_string_list.cpp @@ -0,0 +1,162 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_string_list.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_string_list.h" + +#include "dng_bottlenecks.h" +#include "dng_exceptions.h" +#include "dng_string.h" +#include "dng_utils.h" + +#include "gpr_allocator.h" + +/*****************************************************************************/ + +dng_string_list::dng_string_list () + + : fCount (0) + , fAllocated (0) + , fList (NULL) + + { + + } + +/*****************************************************************************/ + +dng_string_list::~dng_string_list () + { + + Clear (); + + } + +/*****************************************************************************/ + +void dng_string_list::Allocate (uint32 minSize) + { + + if (fAllocated < minSize) + { + + uint32 newSize = Max_uint32 (minSize, fAllocated * 2); + + dng_string **list = (dng_string **)gpr_global_malloc (newSize * sizeof (dng_string *)); + + if (!list) + { + + ThrowMemoryFull (); + + } + + if (fCount) + { + + DoCopyBytes (fList, list, fCount * (uint32) sizeof (dng_string *)); + + } + + if (fList) + { + gpr_global_free( fList ); + } + + fList = list; + + fAllocated = newSize; + + } + + } + +/*****************************************************************************/ + +void dng_string_list::Insert (uint32 index, + const dng_string &s) + { + + Allocate (fCount + 1); + + dng_string *ss = new dng_string (s); + + if (!ss) + { + + ThrowMemoryFull (); + + } + + fCount++; + + for (uint32 j = fCount - 1; j > index; j--) + { + + fList [j] = fList [j - 1]; + + } + + fList [index] = ss; + + } + +/*****************************************************************************/ + +bool dng_string_list::Contains (const dng_string &s) const + { + + for (uint32 j = 0; j < fCount; j++) + { + + if ((*this) [j] == s) + { + + return true; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_string_list::Clear () + { + + if (fList) + { + + for (uint32 index = 0; index < fCount; index++) + { + + delete fList [index]; + + } + + gpr_global_free( fList ); + + fList = NULL; + + } + + fCount = 0; + fAllocated = 0; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_string_list.h b/source/lib/dng_sdk/dng_string_list.h new file mode 100644 index 0000000..c2e78d8 --- /dev/null +++ b/source/lib/dng_sdk/dng_string_list.h @@ -0,0 +1,86 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_string_list.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_string_list__ +#define __dng_string_list__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +class dng_string_list + { + + private: + + uint32 fCount; + + uint32 fAllocated; + + dng_string **fList; + + public: + + dng_string_list (); + + ~dng_string_list (); + + uint32 Count () const + { + return fCount; + } + + dng_string & operator[] (uint32 index) + { + return *(fList [index]); + } + + const dng_string & operator[] (uint32 index) const + { + return *(fList [index]); + } + + void Allocate (uint32 minSize); + + void Insert (uint32 index, + const dng_string &s); + + void Append (const dng_string &s) + { + Insert (Count (), s); + } + + bool Contains (const dng_string &s) const; + + void Clear (); + + private: + + // Hidden copy constructor and assignment operator. + + dng_string_list (const dng_string_list &list); + + dng_string_list & operator= (const dng_string_list &list); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_tag_codes.h b/source/lib/dng_sdk/dng_tag_codes.h new file mode 100644 index 0000000..f1332fc --- /dev/null +++ b/source/lib/dng_sdk/dng_tag_codes.h @@ -0,0 +1,543 @@ +/*****************************************************************************/ +// Copyright 2006-2011 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_tag_codes.h#3 $ */ +/* $DateTime: 2012/05/31 13:27:06 $ */ +/* $Change: 832568 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_tag_codes__ +#define __dng_tag_codes__ + +/*****************************************************************************/ + +// TIFF tags 50706 through 50741 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2003-11-04 & 2003-12-02, purpose "Digital Negative". + +// TIFF tags 50778 through 50781 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2004-08-17, purpose "Digital Negative". + +// TIFF tags 50827 through 50834 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2004-12-06, purpose "Digital Negative". + +// TIFF tag number 50879 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2006-03-23, purpose "Digital Negative". + +// TIFF compression numbers 34892 through 34895 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2003-11-04, purpose "Digital Negative". + +// TIFF tags numbers 50931 through 50942 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2007-04-30, purpose "Digital Negative". + +// TIFF tags numbers 50964 through 50975 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2007-12-17, purpose "Digital Negative". + +// TIFF tags numbers 50981 through 50982 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2008-04-01, purpose "Digital Negative". + +// TIFF tags numbers 51008 through 51009 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2008-10-15, purpose "Digital Negative". + +// TIFF tag number 51022 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2008-12-15, purpose "Digital Negative". + +// TIFF tag number 51041 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2009-5-7, purpose "Digital Negative". + +// TIFF tags numbers 51089 through 51091 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2011-07-01, purpose "Digital Negative". + +// TIFF tags numbers 51107 through 51110 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2011-09-22, purpose "Digital Negative". + +// TIFF tag number 51111 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2011-10-07, purpose "Digital Negative". + +// TIFF tags numbers 51112 through 51114 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2011-10-25, purpose "Digital Negative". + +// TIFF tags number 51125 registered at: +// http://partners.adobe.com/asn/tech/tiff/tiffregister.jsp +// on 2012-05-31, purpose "Digital Negative". + +/*****************************************************************************/ + +// TIFF, DNG, TIFF/EP, and Exif tag codes all share the main TIFF tag code +// number space. In cases where TIFF/EP and Exif have different values for +// tags with the same name, "Exif" is appended to the name of the Exif version +// of the tag. + +enum + { + tcNewSubFileType = 254, + tcSubFileType = 255, + tcImageWidth = 256, + tcImageLength = 257, + tcBitsPerSample = 258, + tcCompression = 259, + tcPhotometricInterpretation = 262, + tcThresholding = 263, + tcCellWidth = 264, + tcCellLength = 265, + tcFillOrder = 266, + tcImageDescription = 270, + tcMake = 271, + tcModel = 272, + tcStripOffsets = 273, + tcOrientation = 274, + tcSamplesPerPixel = 277, + tcRowsPerStrip = 278, + tcStripByteCounts = 279, + tcMinSampleValue = 280, + tcMaxSampleValue = 281, + tcXResolution = 282, + tcYResolution = 283, + tcPlanarConfiguration = 284, + tcFreeOffsets = 285, + tcFreeByteCounts = 286, + tcGrayResponseUnit = 290, + tcGrayResponseCurve = 291, + tcResolutionUnit = 296, + tcTransferFunction = 301, + tcSoftware = 305, + tcDateTime = 306, + tcArtist = 315, + tcHostComputer = 316, + tcPredictor = 317, + tcWhitePoint = 318, + tcPrimaryChromaticities = 319, + tcColorMap = 320, + tcTileWidth = 322, + tcTileLength = 323, + tcTileOffsets = 324, + tcTileByteCounts = 325, + tcSubIFDs = 330, + tcExtraSamples = 338, + tcSampleFormat = 339, + tcJPEGTables = 347, + tcJPEGProc = 512, + tcJPEGInterchangeFormat = 513, + tcJPEGInterchangeFormatLength = 514, + tcYCbCrCoefficients = 529, + tcYCbCrSubSampling = 530, + tcYCbCrPositioning = 531, + tcReferenceBlackWhite = 532, + tcXMP = 700, + tcKodakCameraSerialNumber = 33405, + tcCFARepeatPatternDim = 33421, + tcCFAPattern = 33422, + tcBatteryLevel = 33423, + tcKodakDCRPrivateIFD = 33424, + tcCopyright = 33432, + tcExposureTime = 33434, + tcFNumber = 33437, + tcIPTC_NAA = 33723, + tcLeafPKTS = 34310, + tcAdobeData = 34377, + tcExifIFD = 34665, + tcICCProfile = 34675, + tcExposureProgram = 34850, + tcSpectralSensitivity = 34852, + tcGPSInfo = 34853, + tcISOSpeedRatings = 34855, // EXIF 2.3: PhotographicSensitivity. + tcOECF = 34856, + tcInterlace = 34857, + tcTimeZoneOffset = 34858, + tcSelfTimerMode = 34859, + tcSensitivityType = 34864, + tcStandardOutputSensitivity = 34865, + tcRecommendedExposureIndex = 34866, + tcISOSpeed = 34867, + tcISOSpeedLatitudeyyy = 34868, + tcISOSpeedLatitudezzz = 34869, + tcExifVersion = 36864, + tcDateTimeOriginal = 36867, + tcDateTimeDigitized = 36868, + tcComponentsConfiguration = 37121, + tcCompressedBitsPerPixel = 37122, + tcShutterSpeedValue = 37377, + tcApertureValue = 37378, + tcBrightnessValue = 37379, + tcExposureBiasValue = 37380, + tcMaxApertureValue = 37381, + tcSubjectDistance = 37382, + tcMeteringMode = 37383, + tcLightSource = 37384, + tcFlash = 37385, + tcFocalLength = 37386, + tcFlashEnergy = 37387, + tcSpatialFrequencyResponse = 37388, + tcNoise = 37389, + tcFocalPlaneXResolution = 37390, + tcFocalPlaneYResolution = 37391, + tcFocalPlaneResolutionUnit = 37392, + tcImageNumber = 37393, + tcSecurityClassification = 37394, + tcImageHistory = 37395, + tcSubjectArea = 37396, + tcExposureIndex = 37397, + tcTIFF_EP_StandardID = 37398, + tcSensingMethod = 37399, + tcMakerNote = 37500, + tcUserComment = 37510, + tcSubsecTime = 37520, + tcSubsecTimeOriginal = 37521, + tcSubsecTimeDigitized = 37522, + tcAdobeLayerData = 37724, + tcFlashPixVersion = 40960, + tcColorSpace = 40961, + tcPixelXDimension = 40962, + tcPixelYDimension = 40963, + tcRelatedSoundFile = 40964, + tcInteroperabilityIFD = 40965, + tcFlashEnergyExif = 41483, + tcSpatialFrequencyResponseExif = 41484, + tcFocalPlaneXResolutionExif = 41486, + tcFocalPlaneYResolutionExif = 41487, + tcFocalPlaneResolutionUnitExif = 41488, + tcSubjectLocation = 41492, + tcExposureIndexExif = 41493, + tcSensingMethodExif = 41495, + tcFileSource = 41728, + tcSceneType = 41729, + tcCFAPatternExif = 41730, + tcCustomRendered = 41985, + tcExposureMode = 41986, + tcWhiteBalance = 41987, + tcDigitalZoomRatio = 41988, + tcFocalLengthIn35mmFilm = 41989, + tcSceneCaptureType = 41990, + tcGainControl = 41991, + tcContrast = 41992, + tcSaturation = 41993, + tcSharpness = 41994, + tcDeviceSettingDescription = 41995, + tcSubjectDistanceRange = 41996, + tcImageUniqueID = 42016, + tcCameraOwnerNameExif = 42032, + tcCameraSerialNumberExif = 42033, + tcLensSpecificationExif = 42034, + tcLensMakeExif = 42035, + tcLensModelExif = 42036, + tcLensSerialNumberExif = 42037, + tcGamma = 42240, + tcPrintImageMatchingInfo = 50341, + tcDNGVersion = 50706, + tcDNGBackwardVersion = 50707, + tcUniqueCameraModel = 50708, + tcLocalizedCameraModel = 50709, + tcCFAPlaneColor = 50710, + tcCFALayout = 50711, + tcLinearizationTable = 50712, + tcBlackLevelRepeatDim = 50713, + tcBlackLevel = 50714, + tcBlackLevelDeltaH = 50715, + tcBlackLevelDeltaV = 50716, + tcWhiteLevel = 50717, + tcDefaultScale = 50718, + tcDefaultCropOrigin = 50719, + tcDefaultCropSize = 50720, + tcColorMatrix1 = 50721, + tcColorMatrix2 = 50722, + tcCameraCalibration1 = 50723, + tcCameraCalibration2 = 50724, + tcReductionMatrix1 = 50725, + tcReductionMatrix2 = 50726, + tcAnalogBalance = 50727, + tcAsShotNeutral = 50728, + tcAsShotWhiteXY = 50729, + tcBaselineExposure = 50730, + tcBaselineNoise = 50731, + tcBaselineSharpness = 50732, + tcBayerGreenSplit = 50733, + tcLinearResponseLimit = 50734, + tcCameraSerialNumber = 50735, + tcLensInfo = 50736, + tcChromaBlurRadius = 50737, + tcAntiAliasStrength = 50738, + tcShadowScale = 50739, + tcDNGPrivateData = 50740, + tcMakerNoteSafety = 50741, + tcCalibrationIlluminant1 = 50778, + tcCalibrationIlluminant2 = 50779, + tcBestQualityScale = 50780, + tcRawDataUniqueID = 50781, + tcOriginalRawFileName = 50827, + tcOriginalRawFileData = 50828, + tcActiveArea = 50829, + tcMaskedAreas = 50830, + tcAsShotICCProfile = 50831, + tcAsShotPreProfileMatrix = 50832, + tcCurrentICCProfile = 50833, + tcCurrentPreProfileMatrix = 50834, + tcColorimetricReference = 50879, + tcCameraCalibrationSignature = 50931, + tcProfileCalibrationSignature = 50932, + tcExtraCameraProfiles = 50933, + tcAsShotProfileName = 50934, + tcNoiseReductionApplied = 50935, + tcProfileName = 50936, + tcProfileHueSatMapDims = 50937, + tcProfileHueSatMapData1 = 50938, + tcProfileHueSatMapData2 = 50939, + tcProfileToneCurve = 50940, + tcProfileEmbedPolicy = 50941, + tcProfileCopyright = 50942, + tcForwardMatrix1 = 50964, + tcForwardMatrix2 = 50965, + tcPreviewApplicationName = 50966, + tcPreviewApplicationVersion = 50967, + tcPreviewSettingsName = 50968, + tcPreviewSettingsDigest = 50969, + tcPreviewColorSpace = 50970, + tcPreviewDateTime = 50971, + tcRawImageDigest = 50972, + tcOriginalRawFileDigest = 50973, + tcSubTileBlockSize = 50974, + tcRowInterleaveFactor = 50975, + tcProfileLookTableDims = 50981, + tcProfileLookTableData = 50982, + tcOpcodeList1 = 51008, + tcOpcodeList2 = 51009, + tcOpcodeList3 = 51022, + tcNoiseProfile = 51041, + tcOriginalDefaultFinalSize = 51089, + tcOriginalBestQualityFinalSize = 51090, + tcOriginalDefaultCropSize = 51091, + tcProfileHueSatMapEncoding = 51107, + tcProfileLookTableEncoding = 51108, + tcBaselineExposureOffset = 51109, + tcDefaultBlackRender = 51110, + tcNewRawImageDigest = 51111, + tcRawToPreviewGain = 51112, + tcCacheBlob = 51113, + tcCacheVersion = 51114, + tcDefaultUserCrop = 51125, + tcKodakKDCPrivateIFD = 65024 + }; + +/*****************************************************************************/ + +// Additional values that can be passed as IFD parent codes. + +enum + { + + tcFirstSubIFD = 0x10000, + tcLastSubIFD = 0x1FFFF, + + tcFirstChainedIFD = 0x20000, + tcLastChainedIFD = 0x2FFFF, + + tcFirstMakerNoteIFD = 0x30000, + tcLastMakerNoteIFD = 0x3FFFF, + + tcCanonMakerNote = tcFirstMakerNoteIFD, + tcCasioMakerNote, + tcEpsonMakerNote, + tcFujiMakerNote, + tcHasselbladMakerNote, + tcKodakMakerNote, + tcKodakMakerNote65280, + tcLeicaMakerNote, + tcMamiyaMakerNote, + tcMinoltaMakerNote, + tcNikonMakerNote, + tcOlympusMakerNote, + tcOlympusMakerNote8208, + tcOlympusMakerNote8224, + tcOlympusMakerNote8240, + tcOlympusMakerNote8256, + tcOlympusMakerNote8272, + tcOlympusMakerNote12288, + tcPanasonicMakerNote, + tcPentaxMakerNote, + tcPhaseOneMakerNote, + tcRicohMakerNote, + tcRicohMakerNoteCameraInfo, + tcSamsungMakerNote, + tcSonyMakerNote, + tcSonyMakerNoteSubInfo, + tcSonyPrivateIFD1, + tcSonyPrivateIFD2, + tcSonyPrivateIFD3A, + tcSonyPrivateIFD3B, + tcSonyPrivateIFD3C, + + tcCanonCRW = 0x40000, + tcContaxRAW, + tcContaxHeader, + tcFujiRAF, + tcFujiHeader, + tcFujiRawInfo1, + tcFujiRawInfo2, + tcLeafMOS, + tcMinoltaMRW, + tcPanasonicRAW, + tcFoveonX3F, + tcJPEG, + tcAdobePSD + + }; + +/*****************************************************************************/ + +// GPS tag codes are only valid in the GPS IFD. + +enum + { + tcGPSVersionID = 0, + tcGPSLatitudeRef = 1, + tcGPSLatitude = 2, + tcGPSLongitudeRef = 3, + tcGPSLongitude = 4, + tcGPSAltitudeRef = 5, + tcGPSAltitude = 6, + tcGPSTimeStamp = 7, + tcGPSSatellites = 8, + tcGPSStatus = 9, + tcGPSMeasureMode = 10, + tcGPSDOP = 11, + tcGPSSpeedRef = 12, + tcGPSSpeed = 13, + tcGPSTrackRef = 14, + tcGPSTrack = 15, + tcGPSImgDirectionRef = 16, + tcGPSImgDirection = 17, + tcGPSMapDatum = 18, + tcGPSDestLatitudeRef = 19, + tcGPSDestLatitude = 20, + tcGPSDestLongitudeRef = 21, + tcGPSDestLongitude = 22, + tcGPSDestBearingRef = 23, + tcGPSDestBearing = 24, + tcGPSDestDistanceRef = 25, + tcGPSDestDistance = 26, + tcGPSProcessingMethod = 27, + tcGPSAreaInformation = 28, + tcGPSDateStamp = 29, + tcGPSDifferential = 30, + tcGPSHPositioningError = 31 + }; + +/*****************************************************************************/ + +// Tag codes used in the Interoperability IFD. + +enum + { + tcInteroperabilityIndex = 0x0001, + tcInteroperabilityVersion = 0x0002, + tcRelatedImageFileFormat = 0x1000, + tcRelatedImageWidth = 0x1001, + tcRelatedImageLength = 0x1002 + }; + +/*****************************************************************************/ + +// JPEG marker codes. + +enum JpegMarker + { + + M_TEM = 0x01, + + M_SOF0 = 0xc0, + M_SOF1 = 0xc1, + M_SOF2 = 0xc2, + M_SOF3 = 0xc3, + M_DHT = 0xc4, + M_SOF5 = 0xc5, + M_SOF6 = 0xc6, + M_SOF7 = 0xc7, + M_JPG = 0xc8, + M_SOF9 = 0xc9, + M_SOF10 = 0xca, + M_SOF11 = 0xcb, + M_DAC = 0xcc, + M_SOF13 = 0xcd, + M_SOF14 = 0xce, + M_SOF15 = 0xcf, + + M_RST0 = 0xd0, + M_RST1 = 0xd1, + M_RST2 = 0xd2, + M_RST3 = 0xd3, + M_RST4 = 0xd4, + M_RST5 = 0xd5, + M_RST6 = 0xd6, + M_RST7 = 0xd7, + + M_SOI = 0xd8, + M_EOI = 0xd9, + M_SOS = 0xda, + M_DQT = 0xdb, + M_DNL = 0xdc, + M_DRI = 0xdd, + M_DHP = 0xde, + M_EXP = 0xdf, + + M_APP0 = 0xe0, + M_APP1 = 0xe1, + M_APP2 = 0xe2, + M_APP3 = 0xe3, + M_APP4 = 0xe4, + M_APP5 = 0xe5, + M_APP6 = 0xe6, + M_APP7 = 0xe7, + M_APP8 = 0xe8, + M_APP9 = 0xe9, + M_APP10 = 0xea, + M_APP11 = 0xeb, + M_APP12 = 0xec, + M_APP13 = 0xed, + M_APP14 = 0xee, + M_APP15 = 0xef, + + M_JPG0 = 0xf0, + M_JPG1 = 0xf1, + M_JPG2 = 0xf2, + M_JPG3 = 0xf3, + M_JPG4 = 0xf4, + M_JPG5 = 0xf5, + M_JPG6 = 0xf6, + M_JPG7 = 0xf7, + M_JPG8 = 0xf8, + M_JPG9 = 0xf9, + M_JPG10 = 0xfa, + M_JPG11 = 0xfb, + M_JPG12 = 0xfc, + M_JPG13 = 0xfd, + M_COM = 0xfe, + + M_ERROR = 0x100 + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_tag_types.cpp b/source/lib/dng_sdk/dng_tag_types.cpp new file mode 100644 index 0000000..fcc34e0 --- /dev/null +++ b/source/lib/dng_sdk/dng_tag_types.cpp @@ -0,0 +1,66 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_tag_types.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_tag_types.h" + +/*****************************************************************************/ + +uint32 TagTypeSize (uint32 tagType) + { + + switch (tagType) + { + + case ttByte: + case ttAscii: + case ttSByte: + case ttUndefined: + { + return 1; + } + + case ttShort: + case ttSShort: + case ttUnicode: + { + return 2; + } + + case ttLong: + case ttSLong: + case ttFloat: + case ttIFD: + { + return 4; + } + + case ttRational: + case ttDouble: + case ttSRational: + case ttComplex: + { + return 8; + } + + default: + break; + + } + + return 0; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_tag_types.h b/source/lib/dng_sdk/dng_tag_types.h new file mode 100644 index 0000000..46fd6c5 --- /dev/null +++ b/source/lib/dng_sdk/dng_tag_types.h @@ -0,0 +1,52 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_tag_types.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_tag_types__ +#define __dng_tag_types__ + +/*****************************************************************************/ + +#include "dng_types.h" + +/*****************************************************************************/ + +enum + { + ttByte = 1, + ttAscii, + ttShort, + ttLong, + ttRational, + ttSByte, + ttUndefined, + ttSShort, + ttSLong, + ttSRational, + ttFloat, + ttDouble, + ttIFD, + ttUnicode, + ttComplex + }; + +/*****************************************************************************/ + +uint32 TagTypeSize (uint32 tagType); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_tag_values.h b/source/lib/dng_sdk/dng_tag_values.h new file mode 100644 index 0000000..291b29e --- /dev/null +++ b/source/lib/dng_sdk/dng_tag_values.h @@ -0,0 +1,485 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_tag_values.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_tag_values__ +#define __dng_tag_values__ + +/*****************************************************************************/ + +#include "dng_flags.h" + +/*****************************************************************************/ + +// Values for NewSubFileType tag. + +enum + { + + // The main image data. + + sfMainImage = 0, + + // Preview image for the primary settings. + + sfPreviewImage = 1, + + // Transparency mask + + sfTransparencyMask = 4, + + // Preview Transparency mask + + sfPreviewMask = sfPreviewImage + sfTransparencyMask, + + // Preview image for non-primary settings. + + sfAltPreviewImage = 0x10001 + + }; + +/******************************************************************************/ + +// Values for PhotometricInterpretation tag. + +enum + { + + piWhiteIsZero = 0, + piBlackIsZero = 1, + piRGB = 2, + piRGBPalette = 3, + piTransparencyMask = 4, + piCMYK = 5, + piYCbCr = 6, + piCIELab = 8, + piICCLab = 9, + + piCFA = 32803, // TIFF-EP spec + + piLinearRaw = 34892 + + }; + +/******************************************************************************/ + +// Values for PlanarConfiguration tag. + +enum + { + + pcInterleaved = 1, + pcPlanar = 2, + + pcRowInterleaved = 100000 // Internal use only + + }; + +/******************************************************************************/ + +// Values for ExtraSamples tag. + +enum + { + + esUnspecified = 0, + esAssociatedAlpha = 1, + esUnassociatedAlpha = 2 + + }; + +/******************************************************************************/ + +// Values for SampleFormat tag. + +enum + { + + sfUnsignedInteger = 1, + sfSignedInteger = 2, + sfFloatingPoint = 3, + sfUndefined = 4 + + }; + +/******************************************************************************/ + +// Values for Compression tag. + +enum + { + + ccUncompressed = 1, + ccLZW = 5, + ccOldJPEG = 6, + ccJPEG = 7, + ccDeflate = 8, + +#if GPR_WRITING || GPR_READING + ccVc5 = 9, // Vc5 compression type +#endif + + ccPackBits = 32773, + ccOldDeflate = 32946, + + // Used in DNG files in places that allow lossless JPEG. + + ccLossyJPEG = 34892 + + }; + +/******************************************************************************/ + +// Values for Predictor tag. + +enum + { + + cpNullPredictor = 1, + cpHorizontalDifference = 2, + cpFloatingPoint = 3, + + cpHorizontalDifferenceX2 = 34892, + cpHorizontalDifferenceX4 = 34893, + cpFloatingPointX2 = 34894, + cpFloatingPointX4 = 34895 + + }; + +/******************************************************************************/ + +// Values for ResolutionUnit tag. + +enum + { + + ruNone = 1, + ruInch = 2, + ruCM = 3, + ruMM = 4, + ruMicroM = 5 + + }; + +/******************************************************************************/ + +// Values for LightSource tag. + +enum + { + + lsUnknown = 0, + + lsDaylight = 1, + lsFluorescent = 2, + lsTungsten = 3, + lsFlash = 4, + lsFineWeather = 9, + lsCloudyWeather = 10, + lsShade = 11, + lsDaylightFluorescent = 12, // D 5700 - 7100K + lsDayWhiteFluorescent = 13, // N 4600 - 5500K + lsCoolWhiteFluorescent = 14, // W 3800 - 4500K + lsWhiteFluorescent = 15, // WW 3250 - 3800K + lsWarmWhiteFluorescent = 16, // L 2600 - 3250K + lsStandardLightA = 17, + lsStandardLightB = 18, + lsStandardLightC = 19, + lsD55 = 20, + lsD65 = 21, + lsD75 = 22, + lsD50 = 23, + lsISOStudioTungsten = 24, + + lsOther = 255 + + }; + +/******************************************************************************/ + +// Values for ExposureProgram tag. + +enum + { + + epUnidentified = 0, + epManual = 1, + epProgramNormal = 2, + epAperturePriority = 3, + epShutterPriority = 4, + epProgramCreative = 5, + epProgramAction = 6, + epPortraitMode = 7, + epLandscapeMode = 8 + + }; + +/******************************************************************************/ + +// Values for MeteringMode tag. + +enum + { + + mmUnidentified = 0, + mmAverage = 1, + mmCenterWeightedAverage = 2, + mmSpot = 3, + mmMultiSpot = 4, + mmPattern = 5, + mmPartial = 6, + + mmOther = 255 + + }; + +/******************************************************************************/ + +// CFA color codes from the TIFF/EP specification. + +enum ColorKeyCode + { + + colorKeyRed = 0, + colorKeyGreen = 1, + colorKeyBlue = 2, + colorKeyCyan = 3, + colorKeyMagenta = 4, + colorKeyYellow = 5, + colorKeyWhite = 6, + + colorKeyMaxEnum = 0xFF + + }; + +/*****************************************************************************/ + +// Values for the SensitivityType tag. + +enum + { + + stUnknown = 0, + + stStandardOutputSensitivity = 1, + stRecommendedExposureIndex = 2, + stISOSpeed = 3, + stSOSandREI = 4, + stSOSandISOSpeed = 5, + stREIandISOSpeed = 6, + stSOSandREIandISOSpeed = 7 + + }; + +/*****************************************************************************/ + +// Values for the ColorimetricReference tag. It specifies the colorimetric +// reference used for images with PhotometricInterpretation values of CFA +// or LinearRaw. + +enum + { + + // Scene referred (default): + + crSceneReferred = 0, + + // Output referred using the parameters of the ICC profile PCS. + + crICCProfilePCS = 1 + + }; + +/*****************************************************************************/ + +// Values for the ProfileEmbedPolicy tag. + +enum + { + + // Freely embedable and copyable into installations that encounter this + // profile, so long as the profile is only used to process DNG files. + + pepAllowCopying = 0, + + // Can be embeded in a DNG for portable processing, but cannot be used + // to process other files that the profile is not embedded in. + + pepEmbedIfUsed = 1, + + // Can only be used if installed on the machine processing the file. + // Note that this only applies to stand-alone profiles. Profiles that + // are already embedded inside a DNG file allowed to remain embedded + // in that DNG, even if the DNG is resaved. + + pepEmbedNever = 2, + + // No restricts on profile use or embedding. + + pepNoRestrictions = 3 + + }; + +/*****************************************************************************/ + +// Values for the ProfileHueSatMapEncoding and ProfileLookTableEncoding tags. + +enum + { + + // 1. Convert linear ProPhoto RGB values to HSV. + // 2. Use the HSV coordinates to index into the color table. + // 3. Apply color table result to the original HSV values. + // 4. Convert modified HSV values back to linear ProPhoto RGB. + + encoding_Linear = 0, + + // 1. Convert linear ProPhoto RGB values to HSV. + // 2. Encode V coordinate using sRGB encoding curve. + // 3. Use the encoded HSV coordinates to index into the color table. + // 4. Apply color table result to the encoded values from step 2. + // 5. Decode V coordinate using sRGB decoding curve (inverse of step 2). + // 6. Convert HSV values back to linear ProPhoto RGB (inverse of step 1). + + encoding_sRGB = 1 + + }; + +/*****************************************************************************/ + +// Values for the DefaultBlackRender tag. + +enum + { + + // By default, the renderer applies (possibly auto-calculated) black subtraction + // prior to the look table. + + defaultBlackRender_Auto = 0, + + // By default, the renderer does not apply any black subtraction prior to the + // look table. + + defaultBlackRender_None = 1 + + }; + +/*****************************************************************************/ + +// Values for the PreviewColorSpace tag. + +enum PreviewColorSpaceEnum + { + + previewColorSpace_Unknown = 0, + previewColorSpace_GrayGamma22 = 1, + previewColorSpace_sRGB = 2, + previewColorSpace_AdobeRGB = 3, + previewColorSpace_ProPhotoRGB = 4, + + previewColorSpace_LastValid = previewColorSpace_ProPhotoRGB, + + previewColorSpace_MaxEnum = 0xFFFFFFFF + + }; + +/*****************************************************************************/ + +// Values for CacheVersion tag. + +enum + { + + // The low-16 bits are a rendering version number. + + cacheVersionMask = 0x0FFFF, + + // Default cache version. + + cacheVersionDefault = 0x00100, + + // Is this an integer preview of a floating point image? + + cacheVersionDefloated = 0x10000, + + // Is this an flattening preview of an image with tranparency? + + cacheVersionFlattened = 0x20000, + + // Was this preview build using a the default baseline multi-channel + // CFA merge (i.e. only using the first channel)? + + cacheVersionFakeMerge = 0x40000 + + }; + +/*****************************************************************************/ + +// TIFF-style byte order markers. + +enum + { + + byteOrderII = 0x4949, // 'II' + byteOrderMM = 0x4D4D // 'MM' + + }; + +/*****************************************************************************/ + +// "Magic" numbers. + +enum + { + + // DNG related. + + magicTIFF = 42, // TIFF (and DNG) + magicExtendedProfile = 0x4352, // 'CR' + magicRawCache = 1022, // Raw cache (fast load data) + + // Other raw formats - included here so the DNG SDK can parse them. + + magicPanasonic = 85, + magicOlympusA = 0x4F52, + magicOlympusB = 0x5352 + + }; + +/*****************************************************************************/ + +// DNG Version numbers + +enum + { + + dngVersion_None = 0, + + dngVersion_1_0_0_0 = 0x01000000, + dngVersion_1_1_0_0 = 0x01010000, + dngVersion_1_2_0_0 = 0x01020000, + dngVersion_1_3_0_0 = 0x01030000, + dngVersion_1_4_0_0 = 0x01040000, + + dngVersion_Current = dngVersion_1_4_0_0, + + dngVersion_SaveDefault = dngVersion_Current + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_temperature.cpp b/source/lib/dng_sdk/dng_temperature.cpp new file mode 100644 index 0000000..3635d91 --- /dev/null +++ b/source/lib/dng_sdk/dng_temperature.cpp @@ -0,0 +1,259 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_temperature.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +#include "dng_temperature.h" + +#include "dng_xy_coord.h" + +/*****************************************************************************/ + +// Scale factor between distances in uv space to a more user friendly "tint" +// parameter. + +static const real64 kTintScale = -3000.0; + +/*****************************************************************************/ + +// Table from Wyszecki & Stiles, "Color Science", second edition, page 228. + +struct ruvt + { + real64 r; + real64 u; + real64 v; + real64 t; + }; + +static const ruvt kTempTable [] = + { + { 0, 0.18006, 0.26352, -0.24341 }, + { 10, 0.18066, 0.26589, -0.25479 }, + { 20, 0.18133, 0.26846, -0.26876 }, + { 30, 0.18208, 0.27119, -0.28539 }, + { 40, 0.18293, 0.27407, -0.30470 }, + { 50, 0.18388, 0.27709, -0.32675 }, + { 60, 0.18494, 0.28021, -0.35156 }, + { 70, 0.18611, 0.28342, -0.37915 }, + { 80, 0.18740, 0.28668, -0.40955 }, + { 90, 0.18880, 0.28997, -0.44278 }, + { 100, 0.19032, 0.29326, -0.47888 }, + { 125, 0.19462, 0.30141, -0.58204 }, + { 150, 0.19962, 0.30921, -0.70471 }, + { 175, 0.20525, 0.31647, -0.84901 }, + { 200, 0.21142, 0.32312, -1.0182 }, + { 225, 0.21807, 0.32909, -1.2168 }, + { 250, 0.22511, 0.33439, -1.4512 }, + { 275, 0.23247, 0.33904, -1.7298 }, + { 300, 0.24010, 0.34308, -2.0637 }, + { 325, 0.24702, 0.34655, -2.4681 }, + { 350, 0.25591, 0.34951, -2.9641 }, + { 375, 0.26400, 0.35200, -3.5814 }, + { 400, 0.27218, 0.35407, -4.3633 }, + { 425, 0.28039, 0.35577, -5.3762 }, + { 450, 0.28863, 0.35714, -6.7262 }, + { 475, 0.29685, 0.35823, -8.5955 }, + { 500, 0.30505, 0.35907, -11.324 }, + { 525, 0.31320, 0.35968, -15.628 }, + { 550, 0.32129, 0.36011, -23.325 }, + { 575, 0.32931, 0.36038, -40.770 }, + { 600, 0.33724, 0.36051, -116.45 } + }; + +/*****************************************************************************/ + +void dng_temperature::Set_xy_coord (const dng_xy_coord &xy) + { + + // Convert to uv space. + + real64 u = 2.0 * xy.x / (1.5 - xy.x + 6.0 * xy.y); + real64 v = 3.0 * xy.y / (1.5 - xy.x + 6.0 * xy.y); + + // Search for line pair coordinate is between. + + real64 last_dt = 0.0; + + real64 last_dv = 0.0; + real64 last_du = 0.0; + + for (uint32 index = 1; index <= 30; index++) + { + + // Convert slope to delta-u and delta-v, with length 1. + + real64 du = 1.0; + real64 dv = kTempTable [index] . t; + + real64 len = sqrt (1.0 + dv * dv); + + du /= len; + dv /= len; + + // Find delta from black body point to test coordinate. + + real64 uu = u - kTempTable [index] . u; + real64 vv = v - kTempTable [index] . v; + + // Find distance above or below line. + + real64 dt = - uu * dv + vv * du; + + // If below line, we have found line pair. + + if (dt <= 0.0 || index == 30) + { + + // Find fractional weight of two lines. + + if (dt > 0.0) + dt = 0.0; + + dt = -dt; + + real64 f; + + if (index == 1) + { + f = 0.0; + } + else + { + f = dt / (last_dt + dt); + } + + // Interpolate the temperature. + + fTemperature = 1.0E6 / (kTempTable [index - 1] . r * f + + kTempTable [index ] . r * (1.0 - f)); + + // Find delta from black body point to test coordinate. + + uu = u - (kTempTable [index - 1] . u * f + + kTempTable [index ] . u * (1.0 - f)); + + vv = v - (kTempTable [index - 1] . v * f + + kTempTable [index ] . v * (1.0 - f)); + + // Interpolate vectors along slope. + + du = du * (1.0 - f) + last_du * f; + dv = dv * (1.0 - f) + last_dv * f; + + len = sqrt (du * du + dv * dv); + + du /= len; + dv /= len; + + // Find distance along slope. + + fTint = (uu * du + vv * dv) * kTintScale; + + break; + + } + + // Try next line pair. + + last_dt = dt; + + last_du = du; + last_dv = dv; + + } + + } + +/*****************************************************************************/ + +dng_xy_coord dng_temperature::Get_xy_coord () const + { + + dng_xy_coord result; + + // Find inverse temperature to use as index. + + real64 r = 1.0E6 / fTemperature; + + // Convert tint to offset is uv space. + + real64 offset = fTint * (1.0 / kTintScale); + + // Search for line pair containing coordinate. + + for (uint32 index = 0; index <= 29; index++) + { + + if (r < kTempTable [index + 1] . r || index == 29) + { + + // Find relative weight of first line. + + real64 f = (kTempTable [index + 1] . r - r) / + (kTempTable [index + 1] . r - kTempTable [index] . r); + + // Interpolate the black body coordinates. + + real64 u = kTempTable [index ] . u * f + + kTempTable [index + 1] . u * (1.0 - f); + + real64 v = kTempTable [index ] . v * f + + kTempTable [index + 1] . v * (1.0 - f); + + // Find vectors along slope for each line. + + real64 uu1 = 1.0; + real64 vv1 = kTempTable [index] . t; + + real64 uu2 = 1.0; + real64 vv2 = kTempTable [index + 1] . t; + + real64 len1 = sqrt (1.0 + vv1 * vv1); + real64 len2 = sqrt (1.0 + vv2 * vv2); + + uu1 /= len1; + vv1 /= len1; + + uu2 /= len2; + vv2 /= len2; + + // Find vector from black body point. + + real64 uu3 = uu1 * f + uu2 * (1.0 - f); + real64 vv3 = vv1 * f + vv2 * (1.0 - f); + + real64 len3 = sqrt (uu3 * uu3 + vv3 * vv3); + + uu3 /= len3; + vv3 /= len3; + + // Adjust coordinate along this vector. + + u += uu3 * offset; + v += vv3 * offset; + + // Convert to xy coordinates. + + result.x = 1.5 * u / (u - 4.0 * v + 2.0); + result.y = v / (u - 4.0 * v + 2.0); + + break; + + } + + } + + return result; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_temperature.h b/source/lib/dng_sdk/dng_temperature.h new file mode 100644 index 0000000..7d662bd --- /dev/null +++ b/source/lib/dng_sdk/dng_temperature.h @@ -0,0 +1,97 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_temperature.h#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * Representation of color temperature and offset (tint) using black body + * radiator definition. + */ + +#ifndef __dng_temperature__ +#define __dng_temperature__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +class dng_temperature + { + + private: + + real64 fTemperature; + + real64 fTint; + + public: + + dng_temperature () + + : fTemperature (0.0) + , fTint (0.0) + + { + } + + dng_temperature (real64 temperature, + real64 tint) + + : fTemperature (temperature) + , fTint (tint ) + + { + + } + + dng_temperature (const dng_xy_coord &xy) + + : fTemperature (0.0) + , fTint (0.0) + + { + Set_xy_coord (xy); + } + + void SetTemperature (real64 temperature) + { + fTemperature = temperature; + } + + real64 Temperature () const + { + return fTemperature; + } + + void SetTint (real64 tint) + { + fTint = tint; + } + + real64 Tint () const + { + return fTint; + } + + void Set_xy_coord (const dng_xy_coord &xy); + + dng_xy_coord Get_xy_coord () const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_tile_iterator.cpp b/source/lib/dng_sdk/dng_tile_iterator.cpp new file mode 100644 index 0000000..b91a9dc --- /dev/null +++ b/source/lib/dng_sdk/dng_tile_iterator.cpp @@ -0,0 +1,199 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_tile_iterator.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_tile_iterator.h" + +#include "dng_exceptions.h" +#include "dng_image.h" +#include "dng_pixel_buffer.h" +#include "dng_tag_types.h" +#include "dng_utils.h" + +/*****************************************************************************/ + +dng_tile_iterator::dng_tile_iterator (const dng_image &image, + const dng_rect &area) + + : fArea () + , fTileWidth (0) + , fTileHeight (0) + , fTileTop (0) + , fTileLeft (0) + , fRowLeft (0) + , fLeftPage (0) + , fRightPage (0) + , fTopPage (0) + , fBottomPage (0) + , fHorizontalPage (0) + , fVerticalPage (0) + + { + + Initialize (image.RepeatingTile (), + area & image.Bounds ()); + + } + +/*****************************************************************************/ + +dng_tile_iterator::dng_tile_iterator (const dng_point &tileSize, + const dng_rect &area) + + : fArea () + , fTileWidth (0) + , fTileHeight (0) + , fTileTop (0) + , fTileLeft (0) + , fRowLeft (0) + , fLeftPage (0) + , fRightPage (0) + , fTopPage (0) + , fBottomPage (0) + , fHorizontalPage (0) + , fVerticalPage (0) + + { + + dng_rect tile (area); + + tile.b = Min_int32 (tile.b, tile.t + tileSize.v); + tile.r = Min_int32 (tile.r, tile.l + tileSize.h); + + Initialize (tile, + area); + + } + +/*****************************************************************************/ + +dng_tile_iterator::dng_tile_iterator (const dng_rect &tile, + const dng_rect &area) + + : fArea () + , fTileWidth (0) + , fTileHeight (0) + , fTileTop (0) + , fTileLeft (0) + , fRowLeft (0) + , fLeftPage (0) + , fRightPage (0) + , fTopPage (0) + , fBottomPage (0) + , fHorizontalPage (0) + , fVerticalPage (0) + + { + + Initialize (tile, + area); + + } + +/*****************************************************************************/ + +void dng_tile_iterator::Initialize (const dng_rect &tile, + const dng_rect &area) + { + + fArea = area; + + if (area.IsEmpty ()) + { + + fVerticalPage = 0; + fBottomPage = -1; + + return; + + } + + int32 vOffset = tile.t; + int32 hOffset = tile.l; + + int32 tileHeight = tile.b - vOffset; + int32 tileWidth = tile.r - hOffset; + + fTileHeight = tileHeight; + fTileWidth = tileWidth; + + fLeftPage = (fArea.l - hOffset ) / tileWidth; + fRightPage = (fArea.r - hOffset - 1) / tileWidth; + + fHorizontalPage = fLeftPage; + + fTopPage = (fArea.t - vOffset ) / tileHeight; + fBottomPage = (fArea.b - vOffset - 1) / tileHeight; + + fVerticalPage = fTopPage; + + fTileLeft = fHorizontalPage * tileWidth + hOffset; + fTileTop = fVerticalPage * tileHeight + vOffset; + + fRowLeft = fTileLeft; + + } + +/*****************************************************************************/ + +bool dng_tile_iterator::GetOneTile (dng_rect &tile) + { + + if (fVerticalPage > fBottomPage) + { + return false; + } + + if (fVerticalPage > fTopPage) + tile.t = fTileTop; + else + tile.t = fArea.t; + + if (fVerticalPage < fBottomPage) + tile.b = fTileTop + fTileHeight; + else + tile.b = fArea.b; + + if (fHorizontalPage > fLeftPage) + tile.l = fTileLeft; + else + tile.l = fArea.l; + + if (fHorizontalPage < fRightPage) + tile.r = fTileLeft + fTileWidth; + else + tile.r = fArea.r; + + if (fHorizontalPage < fRightPage) + { + fHorizontalPage++; + fTileLeft += fTileWidth; + } + + else + { + + fVerticalPage++; + fTileTop += fTileHeight; + + fHorizontalPage = fLeftPage; + fTileLeft = fRowLeft; + + } + + return true; + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_tile_iterator.h b/source/lib/dng_sdk/dng_tile_iterator.h new file mode 100644 index 0000000..98bdb1c --- /dev/null +++ b/source/lib/dng_sdk/dng_tile_iterator.h @@ -0,0 +1,76 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_tile_iterator.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_tile_iterator__ +#define __dng_tile_iterator__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_point.h" +#include "dng_rect.h" +#include "dng_types.h" + +/*****************************************************************************/ + +class dng_tile_iterator + { + + private: + + dng_rect fArea; + + int32 fTileWidth; + int32 fTileHeight; + + int32 fTileTop; + int32 fTileLeft; + + int32 fRowLeft; + + int32 fLeftPage; + int32 fRightPage; + + int32 fTopPage; + int32 fBottomPage; + + int32 fHorizontalPage; + int32 fVerticalPage; + + public: + + dng_tile_iterator (const dng_image &image, + const dng_rect &area); + + dng_tile_iterator (const dng_point &tileSize, + const dng_rect &area); + + dng_tile_iterator (const dng_rect &tile, + const dng_rect &area); + + bool GetOneTile (dng_rect &tile); + + private: + + void Initialize (const dng_rect &tile, + const dng_rect &area); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_tone_curve.cpp b/source/lib/dng_sdk/dng_tone_curve.cpp new file mode 100644 index 0000000..466ca95 --- /dev/null +++ b/source/lib/dng_sdk/dng_tone_curve.cpp @@ -0,0 +1,138 @@ +/*****************************************************************************/ +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_tone_curve.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_tone_curve.h" + +#include "dng_assertions.h" +#include "dng_spline.h" +#include "dng_utils.h" + +/******************************************************************************/ + +dng_tone_curve::dng_tone_curve () + + : fCoord () + + { + + SetNull (); + + } + +/******************************************************************************/ + +bool dng_tone_curve::operator== (const dng_tone_curve &curve) const + { + + return fCoord == curve.fCoord; + + } + +/******************************************************************************/ + +void dng_tone_curve::SetNull () + { + + fCoord.resize (2); + + fCoord [0].h = 0.0; + fCoord [0].v = 0.0; + + fCoord [1].h = 1.0; + fCoord [1].v = 1.0; + + } + +/******************************************************************************/ + +bool dng_tone_curve::IsNull () const + { + + dng_tone_curve temp; + + return (*this == temp); + + } + +/******************************************************************************/ + +void dng_tone_curve::SetInvalid () + { + + fCoord.clear (); + + } + +/******************************************************************************/ + +bool dng_tone_curve::IsValid () const + { + + if (fCoord.size () < 2) + { + + return false; + + } + + for (uint32 j = 0; j < fCoord.size (); j++) + { + + if (fCoord [j] . h < 0.0 || fCoord [j] . h > 1.0 || + fCoord [j] . v < 0.0 || fCoord [j] . v > 1.0) + { + + return false; + + } + + if (j > 0) + { + + if (fCoord [j] . h <= fCoord [j - 1] . h) + { + + return false; + + } + + } + + } + + return true; + + } + +/******************************************************************************/ + +void dng_tone_curve::Solve (dng_spline_solver &solver) const + { + + solver.Reset (); + + for (uint32 index = 0; index < fCoord.size (); index++) + { + + solver.Add (fCoord [index].h, + fCoord [index].v); + + } + + solver.Solve (); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_tone_curve.h b/source/lib/dng_sdk/dng_tone_curve.h new file mode 100644 index 0000000..e85052f --- /dev/null +++ b/source/lib/dng_sdk/dng_tone_curve.h @@ -0,0 +1,66 @@ +/*****************************************************************************/ +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_tone_curve.h#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * Representation of 1-dimensional tone curve. + */ + +/*****************************************************************************/ + +#ifndef __dng_tone_curve__ +#define __dng_tone_curve__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_point.h" + +#include + +/*****************************************************************************/ + +class dng_tone_curve + { + + public: + + std::vector fCoord; + + public: + + dng_tone_curve (); + + bool operator== (const dng_tone_curve &curve) const; + + bool operator!= (const dng_tone_curve &curve) const + { + return !(*this == curve); + } + + void SetNull (); + + bool IsNull () const; + + void SetInvalid (); + + bool IsValid () const; + + void Solve (dng_spline_solver &solver) const; + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_types.h b/source/lib/dng_sdk/dng_types.h new file mode 100644 index 0000000..2e944c6 --- /dev/null +++ b/source/lib/dng_sdk/dng_types.h @@ -0,0 +1,112 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_types.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_types__ +#define __dng_types__ + +/*****************************************************************************/ + +#include "dng_flags.h" + +/*****************************************************************************/ + +// Standard integer types. + +#ifdef _MSC_VER +#include +#endif + +#include + +/*****************************************************************************/ + +#if qDNGUseStdInt || 1 + +typedef int8_t int8; +typedef int16_t int16; +typedef int32_t int32; +typedef int64_t int64; + +typedef uint8_t uint8; +typedef uint16_t uint16; +typedef uint32_t uint32; +typedef uint64_t uint64; + +#else + +typedef signed char int8; +typedef signed short int16; +#if __LP64__ +typedef signed int int32; +#else +typedef signed long int32; +#endif +typedef signed long long int64; + +typedef unsigned char uint8; +typedef unsigned short uint16; +/*Some Mac OS X 10.5 SDK headers already define uint32.*/ +#ifndef _UINT32 +#if __LP64__ +typedef unsigned int uint32; +#else +typedef unsigned long uint32; +#endif +#define _UINT32 +#endif +typedef unsigned long long uint64; + +#endif + +typedef uintptr_t uintptr; + +/*****************************************************************************/ + +typedef float real32; +typedef double real64; + +/*****************************************************************************/ + +/// \def Build a Macintosh style four-character constant in a compiler safe way. + +#define DNG_CHAR4(a,b,c,d) ((((uint32) a) << 24) |\ + (((uint32) b) << 16) |\ + (((uint32) c) << 8) |\ + (((uint32) d) )) + +/*****************************************************************************/ + +// #include "stdc_includes.h" +#include + +/*****************************************************************************/ + +// Visual Studio now prefers _hypot to hypot + +#ifdef _MSC_VER + +#ifdef hypot +#undef hypot +#endif + +#define hypot _hypot + +#endif + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_uncopyable.h b/source/lib/dng_sdk/dng_uncopyable.h new file mode 100644 index 0000000..f25745a --- /dev/null +++ b/source/lib/dng_sdk/dng_uncopyable.h @@ -0,0 +1,48 @@ +/*****************************************************************************/ +// Copyright 2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_uncopyable.h#1 $ */ +/* $DateTime: 2012/09/05 12:31:51 $ */ +/* $Change: 847652 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_uncopyable__ +#define __dng_uncopyable__ + +/*****************************************************************************/ + +// Virtual base class to prevent object copies. + +class dng_uncopyable + { + + protected: + + dng_uncopyable () + { + } + + ~dng_uncopyable () + { + } + + private: + + dng_uncopyable (const dng_uncopyable &); + + dng_uncopyable & operator= (const dng_uncopyable &); + + }; + +/*****************************************************************************/ + +#endif // __dng_uncopyable__ + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_utils.cpp b/source/lib/dng_sdk/dng_utils.cpp new file mode 100644 index 0000000..d9f7224 --- /dev/null +++ b/source/lib/dng_sdk/dng_utils.cpp @@ -0,0 +1,706 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_utils.cpp#3 $ */ +/* $DateTime: 2012/08/12 15:38:38 $ */ +/* $Change: 842799 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_utils.h" + +#include "dng_area_task.h" +#include "dng_assertions.h" +#include "dng_bottlenecks.h" +#include "dng_host.h" +#include "dng_image.h" +#include "dng_flags.h" +#include "dng_point.h" +#include "dng_rect.h" +#include "dng_tile_iterator.h" + +#if qMacOS && qEnableCarbon +#include +#endif + +#if qiPhone || qMacOS +// these provide timers +#include +#include +#endif + +#if qWinOS +#include +#else +#include +#include // for va_start/va_end +#endif + +#include "stdc_includes.h" + +/*****************************************************************************/ + +#if qDNGDebug + +/*****************************************************************************/ + +#if qMacOS + #define DNG_DEBUG_BREAK __asm__ volatile ("int3") +#elif qWinOS + #if qDNG64Bit + // no inline assembly on Win 64-bit, so use DebugBreak + #define DNG_DEBUG_BREAK DebugBreak() + #else + #define DNG_DEBUG_BREAK __asm__ volatile ("int3") + #endif +#elif qiPhone + // simulator is running on Intel + #if qiPhoneSimulator + #define DNG_DEBUG_BREAK __asm__ volatile ("int3") + #else + // The debugger doesn't restore program counter after this is called. + // Caller must move program counter past line to continue. + // As of iOS5/xCode 4.2, recovery may not be possible. + #define DNG_DEBUG_BREAK __asm__ volatile ("bkpt 1") + #endif +#elif qAndroid + #define DNG_DEBUG_BREAK __asm__ volatile ("bkpt 1") +#elif qLinux + #define DNG_DEBUG_BREAK __asm__ volatile ("int3") +#else + #define DNG_DEBUG_BREAK +#endif + +/*****************************************************************************/ + +bool gPrintAsserts = true; +bool gBreakOnAsserts = true; + +/*****************************************************************************/ + +void dng_show_message (const char *s) + { + + #if qDNGPrintMessages + + // display the message + if (gPrintAsserts) + fprintf (stderr, "%s\n", s); + + #elif qiPhone || qAndroid || qLinux + + if (gPrintAsserts) + fprintf (stderr, "%s\n", s); + + // iOS doesn't print a message to the console like DebugStr and MessageBox do, so we have to do both + // You'll have to advance the program counter manually past this statement + if (gBreakOnAsserts) + DNG_DEBUG_BREAK; + + #elif qMacOS + + if (gBreakOnAsserts) + { + // truncate the to 255 chars + char ss [256]; + + uint32 len = strlen (s); + if (len > 255) + len = 255; + strncpy (&(ss [1]), s, len ); + ss [0] = (unsigned char) len; + + DebugStr ((unsigned char *) ss); + } + else if (gPrintAsserts) + { + fprintf (stderr, "%s\n", s); + } + + #elif qWinOS + + // display a dialog + // This is not thread safe. Multiple message boxes can be launched. + // Should also be launched in its own thread so main msg queue isn't thrown off. + if (gBreakOnAsserts) + MessageBoxA (NULL, (LPSTR) s, NULL, MB_OK); + else if (gPrintAsserts) + fprintf (stderr, "%s\n", s); + + #endif + + } + +/*****************************************************************************/ + +void dng_show_message_f (const char *fmt, ... ) + { + + char buffer [1024]; + + va_list ap; + va_start (ap, fmt); + + vsnprintf (buffer, sizeof (buffer), fmt, ap); + + va_end (ap); + + dng_show_message (buffer); + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ + +real64 TickTimeInSeconds () + { + + #if qWinOS + + // One might think it prudent to cache the frequency here, however + // low-power CPU modes can, and do, change the value returned. + // Thus the frequencey needs to be retrieved each time. + + // Note that the frequency changing can cause the return + // result to jump backwards, which is why the TickCountInSeconds + // (below) also exists. + + // Just plug in laptop when doing timings to minimize this. + // QPC/QPH is a slow call compared to rtdsc. + + #if qImagecore + + // You should be plugged-in when measuring. + + static real64 freqMultiplier = 0.0; + + if (freqMultiplier == 0.0) + { + + LARGE_INTEGER freq; + + QueryPerformanceFrequency (&freq); + + freqMultiplier = 1.0 / (real64) freq.QuadPart; + + } + + #else + + LARGE_INTEGER freq; + + QueryPerformanceFrequency (&freq); + + real64 freqMultiplier = 1.0 / (real64) freq.QuadPart; + + #endif // qImagecore + + LARGE_INTEGER cycles; + + QueryPerformanceCounter (&cycles); + + return (real64) cycles.QuadPart * freqMultiplier; + + #elif qiPhone || qMacOS + + // this is switching Mac to high performance timer + // and this is also the timer for iPhone + + // assume frequency is unchanging, requesting frequency every time call + // is too slow. multiple cores, different frequency ? + + static real64 freqMultiplier = 0.0; + if (freqMultiplier == 0.0) + { + mach_timebase_info_data_t freq; + mach_timebase_info(&freq); + + // converts from nanos to micros + // numer = 125, denom = 3 * 1000 + freqMultiplier = ((real64)freq.numer / (real64)freq.denom) * 1.0e-9; + } + + return mach_absolute_time() * freqMultiplier; + + #elif qAndroid || qLinux + + //this is a fast timer to nanos + struct timespec now; + clock_gettime(CLOCK_MONOTONIC, &now); + return now.tv_sec + (real64)now.tv_nsec * 1.0e-9; + + #else + + // Perhaps a better call exists. (e.g. avoid adjtime effects) + + struct timeval tv; + + gettimeofday (&tv, NULL); + + return tv.tv_sec + (real64)tv.tv_usec * 1.0e-6; + + #endif + + } + +/*****************************************************************************/ + +real64 TickCountInSeconds () + { + + #if qWinOS + + return GetTickCount () * (1.0 / 1000.0); + + #elif qMacOS && qEnableCarbon + + return TickCount () * (1.0 / 60.0); + + #else + + return TickTimeInSeconds (); + + #endif + + } + +/*****************************************************************************/ + +bool gDNGShowTimers = true; + +dng_timer::dng_timer (const char *message) + + : fMessage (message ) + , fStartTime (TickTimeInSeconds ()) + + { + + } + +/*****************************************************************************/ + +dng_timer::~dng_timer () + { + + if (!gDNGShowTimers) + return; + + real64 totalTime = TickTimeInSeconds () - fStartTime; + + fprintf (stderr, "%s: %0.3f sec\n", fMessage, totalTime); + + } + +/*****************************************************************************/ + +real64 MaxSquaredDistancePointToRect (const dng_point_real64 &point, + const dng_rect_real64 &rect) + { + + real64 distSqr = DistanceSquared (point, + rect.TL ()); + + distSqr = Max_real64 (distSqr, + DistanceSquared (point, + rect.BL ())); + + distSqr = Max_real64 (distSqr, + DistanceSquared (point, + rect.BR ())); + + distSqr = Max_real64 (distSqr, + DistanceSquared (point, + rect.TR ())); + + return distSqr; + + } + +/*****************************************************************************/ + +real64 MaxDistancePointToRect (const dng_point_real64 &point, + const dng_rect_real64 &rect) + { + + return sqrt (MaxSquaredDistancePointToRect (point, + rect)); + + } + +/*****************************************************************************/ + +dng_dither::dng_dither () + + : fNoiseBuffer () + + { + + const uint32 kSeed = 1; + + fNoiseBuffer.Allocate (kRNGSize2D * sizeof (uint16)); + + uint16 *buffer = fNoiseBuffer.Buffer_uint16 (); + + uint32 seed = kSeed; + + for (uint32 i = 0; i < kRNGSize2D; i++) + { + + seed = DNG_Random (seed); + + buffer [i] = (uint16) (seed); + + } + + } + +/******************************************************************************/ + +const dng_dither & dng_dither::Get () + { + + static dng_dither dither; + + return dither; + + } + +/*****************************************************************************/ + +void HistogramArea (dng_host & /* host */, + const dng_image &image, + const dng_rect &area, + uint32 *hist, + uint32 maxValue, + uint32 plane) + { + + DNG_ASSERT (image.PixelType () == ttShort, "Unsupported pixel type"); + + DoZeroBytes (hist, (maxValue + 1) * (uint32) sizeof (uint32)); + + dng_rect tile; + + dng_tile_iterator iter (image, area); + + while (iter.GetOneTile (tile)) + { + + dng_const_tile_buffer buffer (image, tile); + + const void *sPtr = buffer.ConstPixel (tile.t, + tile.l, + plane); + + uint32 count0 = 1; + uint32 count1 = tile.H (); + uint32 count2 = tile.W (); + + int32 step0 = 0; + int32 step1 = buffer.fRowStep; + int32 step2 = buffer.fColStep; + + OptimizeOrder (sPtr, + buffer.fPixelSize, + count0, + count1, + count2, + step0, + step1, + step2); + + DNG_ASSERT (count0 == 1, "OptimizeOrder logic error"); + + const uint16 *s1 = (const uint16 *) sPtr; + + for (uint32 row = 0; row < count1; row++) + { + + if (maxValue == 0x0FFFF && step2 == 1) + { + + for (uint32 col = 0; col < count2; col++) + { + + uint32 x = s1 [col]; + + hist [x] ++; + + } + + } + + else + { + + const uint16 *s2 = s1; + + for (uint32 col = 0; col < count2; col++) + { + + uint32 x = s2 [0]; + + if (x <= maxValue) + { + + hist [x] ++; + + } + + s2 += step2; + + } + + } + + s1 += step1; + + } + + } + + } + +/*****************************************************************************/ + +class dng_limit_float_depth_task: public dng_area_task + { + + private: + + const dng_image &fSrcImage; + + dng_image &fDstImage; + + uint32 fBitDepth; + + real32 fScale; + + public: + + dng_limit_float_depth_task (const dng_image &srcImage, + dng_image &dstImage, + uint32 bitDepth, + real32 scale); + + virtual dng_rect RepeatingTile1 () const + { + return fSrcImage.RepeatingTile (); + } + + virtual dng_rect RepeatingTile2 () const + { + return fDstImage.RepeatingTile (); + } + + virtual void Process (uint32 threadIndex, + const dng_rect &tile, + dng_abort_sniffer *sniffer); + + }; + +/*****************************************************************************/ + +dng_limit_float_depth_task::dng_limit_float_depth_task (const dng_image &srcImage, + dng_image &dstImage, + uint32 bitDepth, + real32 scale) + + : fSrcImage (srcImage) + , fDstImage (dstImage) + , fBitDepth (bitDepth) + , fScale (scale) + + { + + } + +/*****************************************************************************/ + +void dng_limit_float_depth_task::Process (uint32 /* threadIndex */, + const dng_rect &tile, + dng_abort_sniffer * /* sniffer */) + { + + dng_const_tile_buffer srcBuffer (fSrcImage, tile); + dng_dirty_tile_buffer dstBuffer (fDstImage, tile); + + uint32 count0 = tile.H (); + uint32 count1 = tile.W (); + uint32 count2 = fDstImage.Planes (); + + int32 sStep0 = srcBuffer.fRowStep; + int32 sStep1 = srcBuffer.fColStep; + int32 sStep2 = srcBuffer.fPlaneStep; + + int32 dStep0 = dstBuffer.fRowStep; + int32 dStep1 = dstBuffer.fColStep; + int32 dStep2 = dstBuffer.fPlaneStep; + + const void *sPtr = srcBuffer.ConstPixel (tile.t, + tile.l, + 0); + + void *dPtr = dstBuffer.DirtyPixel (tile.t, + tile.l, + 0); + + OptimizeOrder (sPtr, + dPtr, + srcBuffer.fPixelSize, + dstBuffer.fPixelSize, + count0, + count1, + count2, + sStep0, + sStep1, + sStep2, + dStep0, + dStep1, + dStep2); + + const real32 *sPtr0 = (const real32 *) sPtr; + real32 *dPtr0 = ( real32 *) dPtr; + + real32 scale = fScale; + + bool limit16 = (fBitDepth == 16); + bool limit24 = (fBitDepth == 24); + + for (uint32 index0 = 0; index0 < count0; index0++) + { + + const real32 *sPtr1 = sPtr0; + real32 *dPtr1 = dPtr0; + + for (uint32 index1 = 0; index1 < count1; index1++) + { + + // If the scale is a NOP, and the data is packed solid, we can just do memory + // copy. + + if (scale == 1.0f && sStep2 == 1 && dStep2 == 1) + { + + if (dPtr1 != sPtr1) // srcImage != dstImage + { + + memcpy (dPtr1, sPtr1, count2 * (uint32) sizeof (real32)); + + } + + } + + else + { + + const real32 *sPtr2 = sPtr1; + real32 *dPtr2 = dPtr1; + + for (uint32 index2 = 0; index2 < count2; index2++) + { + + real32 x = sPtr2 [0]; + + x *= scale; + + dPtr2 [0] = x; + + sPtr2 += sStep2; + dPtr2 += dStep2; + + } + + } + + // The data is now in the destination buffer. + + if (limit16) + { + + uint32 *dPtr2 = (uint32 *) dPtr1; + + for (uint32 index2 = 0; index2 < count2; index2++) + { + + uint32 x = dPtr2 [0]; + + uint16 y = DNG_FloatToHalf (x); + + x = DNG_HalfToFloat (y); + + dPtr2 [0] = x; + + dPtr2 += dStep2; + + } + + } + + else if (limit24) + { + + uint32 *dPtr2 = (uint32 *) dPtr1; + + for (uint32 index2 = 0; index2 < count2; index2++) + { + + uint32 x = dPtr2 [0]; + + uint8 temp [3]; + + DNG_FloatToFP24 (x, temp); + + x = DNG_FP24ToFloat (temp); + + dPtr2 [0] = x; + + dPtr2 += dStep2; + + } + + } + + sPtr1 += sStep1; + dPtr1 += dStep1; + + } + + sPtr0 += sStep0; + dPtr0 += dStep0; + + } + + } + +/******************************************************************************/ + +void LimitFloatBitDepth (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + uint32 bitDepth, + real32 scale) + { + + DNG_ASSERT (srcImage.PixelType () == ttFloat, "Floating point image expected"); + DNG_ASSERT (dstImage.PixelType () == ttFloat, "Floating point image expected"); + + dng_limit_float_depth_task task (srcImage, + dstImage, + bitDepth, + scale); + + host.PerformAreaTask (task, dstImage.Bounds ()); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_utils.h b/source/lib/dng_sdk/dng_utils.h new file mode 100644 index 0000000..560c37c --- /dev/null +++ b/source/lib/dng_sdk/dng_utils.h @@ -0,0 +1,1237 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_utils.h#3 $ */ +/* $DateTime: 2012/06/14 20:24:41 $ */ +/* $Change: 835078 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_utils__ +#define __dng_utils__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_flags.h" +#include "dng_memory.h" +#include "dng_types.h" + +/*****************************************************************************/ + +inline uint32 Abs_int32 (int32 x) + { + + #if 0 + + // Reference version. + + return (uint32) (x < 0 ? -x : x); + + #else + + // Branchless version. + + uint32 mask = (uint32) (x >> 31); + + return (uint32) (((uint32) x + mask) ^ mask); + + #endif + + } + +inline int32 Min_int32 (int32 x, int32 y) + { + + return (x <= y ? x : y); + + } + +inline int32 Max_int32 (int32 x, int32 y) + { + + return (x >= y ? x : y); + + } + +inline int32 Pin_int32 (int32 min, int32 x, int32 max) + { + + return Max_int32 (min, Min_int32 (x, max)); + + } + +inline int32 Pin_int32_between (int32 a, int32 x, int32 b) + { + + int32 min, max; + if (a < b) { min = a; max = b; } + else { min = b; max = a; } + + return Pin_int32 (min, x, max); + + } + +/*****************************************************************************/ + +inline uint16 Min_uint16 (uint16 x, uint16 y) + { + + return (x <= y ? x : y); + + } + +inline uint16 Max_uint16 (uint16 x, uint16 y) + { + + return (x >= y ? x : y); + + } + +inline int16 Pin_int16 (int32 x) + { + + x = Pin_int32 (-32768, x, 32767); + + return (int16) x; + + } + +/*****************************************************************************/ + +inline uint32 Min_uint32 (uint32 x, uint32 y) + { + + return (x <= y ? x : y); + + } + +inline uint32 Min_uint32 (uint32 x, uint32 y, uint32 z) + { + + return Min_uint32 (x, Min_uint32 (y, z)); + + } + +inline uint32 Max_uint32 (uint32 x, uint32 y) + { + + return (x >= y ? x : y); + + } + +inline uint32 Max_uint32 (uint32 x, uint32 y, uint32 z) + { + + return Max_uint32 (x, Max_uint32 (y, z)); + + } + +inline uint32 Pin_uint32 (uint32 min, uint32 x, uint32 max) + { + + return Max_uint32 (min, Min_uint32 (x, max)); + + } + +/*****************************************************************************/ + +inline uint16 Pin_uint16 (int32 x) + { + + #if 0 + + // Reference version. + + x = Pin_int32 (0, x, 0x0FFFF); + + #else + + // Single branch version. + + if (x & ~65535) + { + + x = ~x >> 31; + + } + + #endif + + return (uint16) x; + + } + +/*****************************************************************************/ + +inline uint32 RoundUp2 (uint32 x) + { + + return (x + 1) & (uint32) ~1; + + } + +inline uint32 RoundUp4 (uint32 x) + { + + return (x + 3) & (uint32) ~3; + + } + +inline uint32 RoundUp8 (uint32 x) + { + + return (x + 7) & (uint32) ~7; + + } + +inline uint32 RoundUp16 (uint32 x) + { + + return (x + 15) & (uint32) ~15; + + } + +inline uint32 RoundUp4096 (uint32 x) + { + + return (x + 4095) & (uint32) ~4095; + + } + +/******************************************************************************/ + +inline uint32 RoundDown2 (uint32 x) + { + + return x & (uint32) ~1; + + } + +inline uint32 RoundDown4 (uint32 x) + { + + return x & (uint32) ~3; + + } + +inline uint32 RoundDown8 (uint32 x) + { + + return x & (uint32) ~7; + + } + +inline uint32 RoundDown16 (uint32 x) + { + + return x & (uint32) ~15; + + } + +/******************************************************************************/ + +inline uint32 RoundUpForPixelSize (uint32 x, uint32 pixelSize) + { + + switch (pixelSize) + { + + case 1: + return RoundUp16 (x); + + case 2: + return RoundUp8 (x); + + case 4: + return RoundUp4 (x); + + case 8: + return RoundUp2 (x); + + default: + return RoundUp16 (x); + + } + + } + +/******************************************************************************/ + +inline uint64 Abs_int64 (int64 x) + { + + return (uint64) (x < 0 ? -x : x); + + } + +inline int64 Min_int64 (int64 x, int64 y) + { + + return (x <= y ? x : y); + + } + +inline int64 Max_int64 (int64 x, int64 y) + { + + return (x >= y ? x : y); + + } + +inline int64 Pin_int64 (int64 min, int64 x, int64 max) + { + + return Max_int64 (min, Min_int64 (x, max)); + + } + +/******************************************************************************/ + +inline uint64 Min_uint64 (uint64 x, uint64 y) + { + + return (x <= y ? x : y); + + } + +inline uint64 Max_uint64 (uint64 x, uint64 y) + { + + return (x >= y ? x : y); + + } + +inline uint64 Pin_uint64 (uint64 min, uint64 x, uint64 max) + { + + return Max_uint64 (min, Min_uint64 (x, max)); + + } + +/*****************************************************************************/ + +inline real32 Abs_real32 (real32 x) + { + + return (x < 0.0f ? -x : x); + + } + +inline real32 Min_real32 (real32 x, real32 y) + { + + return (x < y ? x : y); + + } + +inline real32 Max_real32 (real32 x, real32 y) + { + + return (x > y ? x : y); + + } + +inline real32 Pin_real32 (real32 min, real32 x, real32 max) + { + + return Max_real32 (min, Min_real32 (x, max)); + + } + +inline real32 Pin_real32 (real32 x) + { + + return Pin_real32 (0.0f, x, 1.0f); + + } + +inline real32 Pin_real32_Overrange (real32 min, + real32 x, + real32 max) + { + + // Normal numbers in (min,max). No change. + + if (x > min && x < max) + { + return x; + } + + // Map large numbers (including positive infinity) to max. + + else if (x > min) + { + return max; + } + + // Map everything else (including negative infinity and all NaNs) to min. + + return min; + + } + +inline real32 Pin_Overrange (real32 x) + { + + // Normal in-range numbers, except for plus and minus zero. + + if (x > 0.0f && x <= 1.0f) + { + return x; + } + + // Large numbers, including positive infinity. + + else if (x > 0.5f) + { + return 1.0f; + } + + // Plus and minus zero, negative numbers, negative infinity, and all NaNs. + + return 0.0f; + + } + +inline real32 Lerp_real32 (real32 a, real32 b, real32 t) + { + + return a + t * (b - a); + + } + +/*****************************************************************************/ + +inline real64 Abs_real64 (real64 x) + { + + return (x < 0.0 ? -x : x); + + } + +inline real64 Min_real64 (real64 x, real64 y) + { + + return (x < y ? x : y); + + } + +inline real64 Max_real64 (real64 x, real64 y) + { + + return (x > y ? x : y); + + } + +inline real64 Pin_real64 (real64 min, real64 x, real64 max) + { + + return Max_real64 (min, Min_real64 (x, max)); + + } + +inline real64 Pin_real64 (real64 x) + { + + return Pin_real64 (0.0, x, 1.0); + + } + +inline real64 Pin_real64_Overrange (real64 min, + real64 x, + real64 max) + { + + // Normal numbers in (min,max). No change. + + if (x > min && x < max) + { + return x; + } + + // Map large numbers (including positive infinity) to max. + + else if (x > min) + { + return max; + } + + // Map everything else (including negative infinity and all NaNs) to min. + + return min; + + } + +inline real64 Lerp_real64 (real64 a, real64 b, real64 t) + { + + return a + t * (b - a); + + } + +/*****************************************************************************/ + +inline int32 Round_int32 (real32 x) + { + + return (int32) (x > 0.0f ? x + 0.5f : x - 0.5f); + + } + +inline int32 Round_int32 (real64 x) + { + + return (int32) (x > 0.0 ? x + 0.5 : x - 0.5); + + } + +inline uint32 Floor_uint32 (real32 x) + { + + return (uint32) Max_real32 (0.0f, x); + + } + +inline uint32 Floor_uint32 (real64 x) + { + + return (uint32) Max_real64 (0.0, x); + + } + +inline uint32 Round_uint32 (real32 x) + { + + return Floor_uint32 (x + 0.5f); + + } + +inline uint32 Round_uint32 (real64 x) + { + + return Floor_uint32 (x + 0.5); + + } + +/******************************************************************************/ + +inline int64 Round_int64 (real64 x) + { + + return (int64) (x >= 0.0 ? x + 0.5 : x - 0.5); + + } + +/*****************************************************************************/ + +const int64 kFixed64_One = (((int64) 1) << 32); +const int64 kFixed64_Half = (((int64) 1) << 31); + +/******************************************************************************/ + +inline int64 Real64ToFixed64 (real64 x) + { + + return Round_int64 (x * (real64) kFixed64_One); + + } + +/******************************************************************************/ + +inline real64 Fixed64ToReal64 (int64 x) + { + + return x * (1.0 / (real64) kFixed64_One); + + } + +/*****************************************************************************/ + +inline char ForceUppercase (char c) + { + + if (c >= 'a' && c <= 'z') + { + + c -= 'a' - 'A'; + + } + + return c; + + } + +/*****************************************************************************/ + +inline uint16 SwapBytes16 (uint16 x) + { + + return (uint16) ((x << 8) | + (x >> 8)); + + } + +inline uint32 SwapBytes32 (uint32 x) + { + + return (x << 24) + + ((x << 8) & 0x00FF0000) + + ((x >> 8) & 0x0000FF00) + + (x >> 24); + + } + +/*****************************************************************************/ + +inline bool IsAligned16 (const void *p) + { + + return (((uintptr) p) & 1) == 0; + + } + +inline bool IsAligned32 (const void *p) + { + + return (((uintptr) p) & 3) == 0; + + } + +inline bool IsAligned64 (const void *p) + { + + return (((uintptr) p) & 7) == 0; + + } + +inline bool IsAligned128 (const void *p) + { + + return (((uintptr) p) & 15) == 0; + + } + +/******************************************************************************/ + +// Converts from RGB values (range 0.0 to 1.0) to HSV values (range 0.0 to +// 6.0 for hue, and 0.0 to 1.0 for saturation and value). + +inline void DNG_RGBtoHSV (real32 r, + real32 g, + real32 b, + real32 &h, + real32 &s, + real32 &v) + { + + v = Max_real32 (r, Max_real32 (g, b)); + + real32 gap = v - Min_real32 (r, Min_real32 (g, b)); + + if (gap > 0.0f) + { + + if (r == v) + { + + h = (g - b) / gap; + + if (h < 0.0f) + { + h += 6.0f; + } + + } + + else if (g == v) + { + h = 2.0f + (b - r) / gap; + } + + else + { + h = 4.0f + (r - g) / gap; + } + + s = gap / v; + + } + + else + { + h = 0.0f; + s = 0.0f; + } + + } + +/*****************************************************************************/ + +// Converts from HSV values (range 0.0 to 6.0 for hue, and 0.0 to 1.0 for +// saturation and value) to RGB values (range 0.0 to 1.0). + +inline void DNG_HSVtoRGB (real32 h, + real32 s, + real32 v, + real32 &r, + real32 &g, + real32 &b) + { + + if (s > 0.0f) + { + + if (h < 0.0f) + h += 6.0f; + + if (h >= 6.0f) + h -= 6.0f; + + int32 i = (int32) h; + real32 f = h - (real32) i; + + real32 p = v * (1.0f - s); + + #define q (v * (1.0f - s * f)) + #define t (v * (1.0f - s * (1.0f - f))) + + switch (i) + { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + } + + #undef q + #undef t + + } + + else + { + r = v; + g = v; + b = v; + } + + } + +/******************************************************************************/ + +// High resolution timer, for code profiling. + +real64 TickTimeInSeconds (); + +// Lower resolution timer, but more stable. + +real64 TickCountInSeconds (); + +/******************************************************************************/ + +class dng_timer + { + + public: + + dng_timer (const char *message); + + ~dng_timer (); + + private: + + // Hidden copy constructor and assignment operator. + + dng_timer (const dng_timer &timer); + + dng_timer & operator= (const dng_timer &timer); + + private: + + const char *fMessage; + + real64 fStartTime; + + }; + +/*****************************************************************************/ + +// Returns the maximum squared Euclidean distance from the specified point to the +// specified rectangle rect. + +real64 MaxSquaredDistancePointToRect (const dng_point_real64 &point, + const dng_rect_real64 &rect); + +/*****************************************************************************/ + +// Returns the maximum Euclidean distance from the specified point to the specified +// rectangle rect. + +real64 MaxDistancePointToRect (const dng_point_real64 &point, + const dng_rect_real64 &rect); + +/*****************************************************************************/ + +inline uint32 DNG_HalfToFloat (uint16 halfValue) + { + + int32 sign = (halfValue >> 15) & 0x00000001; + int32 exponent = (halfValue >> 10) & 0x0000001f; + int32 mantissa = halfValue & 0x000003ff; + + if (exponent == 0) + { + + if (mantissa == 0) + { + + // Plus or minus zero + + return (uint32) (sign << 31); + + } + + else + { + + // Denormalized number -- renormalize it + + while (!(mantissa & 0x00000400)) + { + mantissa <<= 1; + exponent -= 1; + } + + exponent += 1; + mantissa &= ~0x00000400; + + } + + } + + else if (exponent == 31) + { + + if (mantissa == 0) + { + + // Positive or negative infinity, convert to maximum (16 bit) values. + + return (uint32) ((sign << 31) | ((0x1eL + 127 - 15) << 23) | (0x3ffL << 13)); + + } + + else + { + + // Nan -- Just set to zero. + + return 0; + + } + + } + + // Normalized number + + exponent += (127 - 15); + mantissa <<= 13; + + // Assemble sign, exponent and mantissa. + + return (uint32) ((sign << 31) | (exponent << 23) | mantissa); + + } + +/*****************************************************************************/ + +inline uint16 DNG_FloatToHalf (uint32 i) + { + + int32 sign = (i >> 16) & 0x00008000; + int32 exponent = ((i >> 23) & 0x000000ff) - (127 - 15); + int32 mantissa = i & 0x007fffff; + + if (exponent <= 0) + { + + if (exponent < -10) + { + + // Zero or underflow to zero. + + return (uint16)sign; + + } + + // E is between -10 and 0. We convert f to a denormalized half. + + mantissa = (mantissa | 0x00800000) >> (1 - exponent); + + // Round to nearest, round "0.5" up. + // + // Rounding may cause the significand to overflow and make + // our number normalized. Because of the way a half's bits + // are laid out, we don't have to treat this case separately; + // the code below will handle it correctly. + + if (mantissa & 0x00001000) + mantissa += 0x00002000; + + // Assemble the half from sign, exponent (zero) and mantissa. + + return (uint16)(sign | (mantissa >> 13)); + + } + + else if (exponent == 0xff - (127 - 15)) + { + + if (mantissa == 0) + { + + // F is an infinity; convert f to a half + // infinity with the same sign as f. + + return (uint16)(sign | 0x7c00); + + } + + else + { + + // F is a NAN; produce a half NAN that preserves + // the sign bit and the 10 leftmost bits of the + // significand of f. + + return (uint16)(sign | 0x7c00 | (mantissa >> 13)); + + } + + } + + // E is greater than zero. F is a normalized float. + // We try to convert f to a normalized half. + + // Round to nearest, round "0.5" up + + if (mantissa & 0x00001000) + { + + mantissa += 0x00002000; + + if (mantissa & 0x00800000) + { + mantissa = 0; // overflow in significand, + exponent += 1; // adjust exponent + } + + } + + // Handle exponent overflow + + if (exponent > 30) + { + return (uint16)(sign | 0x7c00); // infinity with the same sign as f. + } + + // Assemble the half from sign, exponent and mantissa. + + return (uint16)(sign | (exponent << 10) | (mantissa >> 13)); + + } + +/*****************************************************************************/ + +inline uint32 DNG_FP24ToFloat (const uint8 *input) + { + + int32 sign = (input [0] >> 7) & 0x01; + int32 exponent = (input [0] ) & 0x7F; + int32 mantissa = (((int32) input [1]) << 8) | input[2]; + + if (exponent == 0) + { + + if (mantissa == 0) + { + + // Plus or minus zero + + return (uint32) (sign << 31); + + } + + else + { + + // Denormalized number -- renormalize it + + while (!(mantissa & 0x00010000)) + { + mantissa <<= 1; + exponent -= 1; + } + + exponent += 1; + mantissa &= ~0x00010000; + + } + + } + + else if (exponent == 127) + { + + if (mantissa == 0) + { + + // Positive or negative infinity, convert to maximum (24 bit) values. + + return (uint32) ((sign << 31) | ((0x7eL + 128 - 64) << 23) | (0xffffL << 7)); + + } + + else + { + + // Nan -- Just set to zero. + + return 0; + + } + + } + + // Normalized number + + exponent += (128 - 64); + mantissa <<= 7; + + // Assemble sign, exponent and mantissa. + + return (uint32) ((sign << 31) | (exponent << 23) | mantissa); + + } + +/*****************************************************************************/ + +inline void DNG_FloatToFP24 (uint32 input, uint8 *output) + { + + int32 exponent = (int32) ((input >> 23) & 0xFF) - 128; + int32 mantissa = input & 0x007FFFFF; + + if (exponent == 127) // infinity or NaN + { + + // Will the NaN alais to infinity? + + if (mantissa != 0x007FFFFF && ((mantissa >> 7) == 0xFFFF)) + { + + mantissa &= 0x003FFFFF; // knock out msb to make it a NaN + + } + + } + + else if (exponent > 63) // overflow, map to infinity + { + + exponent = 63; + mantissa = 0x007FFFFF; + + } + + else if (exponent <= -64) + { + + if (exponent >= -79) // encode as denorm + { + mantissa = (mantissa | 0x00800000) >> (-63 - exponent); + } + + else // underflow to zero + { + mantissa = 0; + } + + exponent = -64; + + } + + output [0] = (uint8)(((input >> 24) & 0x80) | (uint32) (exponent + 64)); + + output [1] = (mantissa >> 15) & 0x00FF; + output [2] = (mantissa >> 7) & 0x00FF; + + } + +/******************************************************************************/ + +// The following code was from PSDivide.h in Photoshop. + +// High order 32-bits of an unsigned 32 by 32 multiply. + +#ifndef MULUH + +#if defined(_X86_) && defined(_MSC_VER) + +inline uint32 Muluh86 (uint32 x, uint32 y) + { + uint32 result; + __asm + { + MOV EAX, x + MUL y + MOV result, EDX + } + return (result); + } + +#define MULUH Muluh86 + +#else + +#define MULUH(x,y) ((uint32) (((x) * (uint64) (y)) >> 32)) + +#endif + +#endif + +// High order 32-bits of an signed 32 by 32 multiply. + +#ifndef MULSH + +#if defined(_X86_) && defined(_MSC_VER) + +inline int32 Mulsh86 (int32 x, int32 y) + { + int32 result; + __asm + { + MOV EAX, x + IMUL y + MOV result, EDX + } + return (result); + } + +#define MULSH Mulsh86 + +#else + +#define MULSH(x,y) ((int32) (((x) * (int64) (y)) >> 32)) + +#endif + +#endif + +/******************************************************************************/ + +// Random number generator (identical to Apple's) for portable use. + +// This implements the "minimal standard random number generator" +// as proposed by Park and Miller in CACM October, 1988. +// It has a period of 2147483647 (0x7fffffff) + +// This is the ACM standard 30 bit generator: +// x' = (x * 16807) mod 2^31-1 + +inline uint32 DNG_Random (uint32 seed) + { + + // high = seed / 127773 + + uint32 temp = MULUH (0x069C16BD, seed); + uint32 high = (temp + ((seed - temp) >> 1)) >> 16; + + // low = seed % 127773 + + uint32 low = seed - high * 127773; + + // seed = (seed * 16807) % 2147483647 + + seed = 16807 * low - 2836 * high; + + if (seed & 0x80000000) + seed += 2147483647; + + return seed; + + } + +/*****************************************************************************/ + +class dng_dither + { + + public: + + static const uint32 kRNGBits = 7; + + static const uint32 kRNGSize = 1 << kRNGBits; + + static const uint32 kRNGMask = kRNGSize - 1; + + static const uint32 kRNGSize2D = kRNGSize * kRNGSize; + + private: + + dng_memory_data fNoiseBuffer; + + private: + + dng_dither (); + + // Hidden copy constructor and assignment operator. + + dng_dither (const dng_dither &); + + dng_dither & operator= (const dng_dither &); + + public: + + static const dng_dither & Get (); + + public: + + const uint16 *NoiseBuffer16 () const + { + return fNoiseBuffer.Buffer_uint16 (); + } + + }; + +/*****************************************************************************/ + +void HistogramArea (dng_host &host, + const dng_image &image, + const dng_rect &area, + uint32 *hist, + uint32 histLimit, + uint32 plane = 0); + +/*****************************************************************************/ + +void LimitFloatBitDepth (dng_host &host, + const dng_image &srcImage, + dng_image &dstImage, + uint32 bitDepth, + real32 scale = 1.0f); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_validate.cpp b/source/lib/dng_sdk/dng_validate.cpp new file mode 100644 index 0000000..468968f --- /dev/null +++ b/source/lib/dng_sdk/dng_validate.cpp @@ -0,0 +1,879 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_validate.cpp#2 $ */ +/* $DateTime: 2012/06/14 20:24:41 $ */ +/* $Change: 835078 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_color_space.h" +#include "dng_date_time.h" +#include "dng_exceptions.h" +#include "dng_file_stream.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_ifd.h" +#include "dng_image_writer.h" +#include "dng_info.h" +#include "dng_linearization_info.h" +#include "dng_mosaic_info.h" +#include "dng_negative.h" +#include "dng_preview.h" +#include "dng_render.h" +#include "dng_simple_image.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_xmp.h" +#include "dng_xmp_sdk.h" + +/*****************************************************************************/ + +#if qDNGValidateTarget + +/*****************************************************************************/ + +#define kDNGValidateVersion "1.4" + +/*****************************************************************************/ + +static bool gFourColorBayer = false; + +static int32 gMosaicPlane = -1; + +static uint32 gPreferredSize = 0; +static uint32 gMinimumSize = 0; +static uint32 gMaximumSize = 0; + +static uint32 gProxyDNGSize = 0; + +static const dng_color_space *gFinalSpace = &dng_space_sRGB::Get (); + +static uint32 gFinalPixelType = ttByte; + +static dng_string gDumpStage1; +static dng_string gDumpStage2; +static dng_string gDumpStage3; +static dng_string gDumpTIF; +static dng_string gDumpDNG; + +/*****************************************************************************/ + +static dng_error_code dng_validate (const char *filename) + { + + printf ("Validating \"%s\"...\n", filename); + + try + { + + dng_file_stream stream (filename); + + dng_host host; + + host.SetPreferredSize (gPreferredSize); + host.SetMinimumSize (gMinimumSize ); + host.SetMaximumSize (gMaximumSize ); + + host.ValidateSizes (); + + if (host.MinimumSize ()) + { + + host.SetForPreview (true); + + gDumpDNG.Clear (); + + } + + if (gDumpDNG.NotEmpty ()) + { + + host.SetSaveDNGVersion (dngVersion_SaveDefault); + + host.SetSaveLinearDNG (false); + + host.SetKeepOriginalFile (false); + + } + + // Read into the negative. + + AutoPtr negative; + + { + + dng_info info; + + info.Parse (host, stream); + + info.PostParse (host); + + if (!info.IsValidDNG ()) + { + return dng_error_bad_format; + } + + negative.Reset (host.Make_dng_negative ()); + + negative->Parse (host, stream, info); + + negative->PostParse (host, stream, info); + + { + + dng_timer timer ("Raw image read time"); + + negative->ReadStage1Image (host, stream, info); + + } + + if (info.fMaskIndex != -1) + { + + dng_timer timer ("Transparency mask read time"); + + negative->ReadTransparencyMask (host, stream, info); + + } + + negative->ValidateRawImageDigest (host); + + } + + // Option to write stage 1 image. + + if (gDumpStage1.NotEmpty ()) + { + + dng_file_stream stream2 (gDumpStage1.Get (), true); + + const dng_image &stage1 = *negative->Stage1Image (); + + dng_image_writer writer; + + writer.WriteTIFF (host, + stream2, + stage1, + stage1.Planes () >= 3 ? piRGB + : piBlackIsZero); + + gDumpStage1.Clear (); + + } + + // Metadata. + + negative->SynchronizeMetadata (); + + // Four color Bayer option. + + if (gFourColorBayer) + { + negative->SetFourColorBayer (); + } + + // Build stage 2 image. + + { + + dng_timer timer ("Linearization time"); + + negative->BuildStage2Image (host); + + } + + if (gDumpStage2.NotEmpty ()) + { + + dng_file_stream stream2 (gDumpStage2.Get (), true); + + const dng_image &stage2 = *negative->Stage2Image (); + + dng_image_writer writer; + + writer.WriteTIFF (host, + stream2, + stage2, + stage2.Planes () >= 3 ? piRGB + : piBlackIsZero); + + gDumpStage2.Clear (); + + } + + // Build stage 3 image. + + { + + dng_timer timer ("Interpolate time"); + + negative->BuildStage3Image (host, + gMosaicPlane); + + } + + // Convert to proxy, if requested. + + if (gProxyDNGSize) + { + + dng_timer timer ("ConvertToProxy time"); + + dng_image_writer writer; + + negative->ConvertToProxy (host, + writer, + gProxyDNGSize); + + } + + // Flatten transparency, if required. + + if (negative->NeedFlattenTransparency (host)) + { + + dng_timer timer ("FlattenTransparency time"); + + negative->FlattenTransparency (host); + + } + + if (gDumpStage3.NotEmpty ()) + { + + dng_file_stream stream2 (gDumpStage3.Get (), true); + + const dng_image &stage3 = *negative->Stage3Image (); + + dng_image_writer writer; + + writer.WriteTIFF (host, + stream2, + stage3, + stage3.Planes () >= 3 ? piRGB + : piBlackIsZero); + + gDumpStage3.Clear (); + + } + + // Output DNG file if requested. + + if (gDumpDNG.NotEmpty ()) + { + + // Build the preview list. + + dng_preview_list previewList; + + dng_date_time_info dateTimeInfo; + + CurrentDateTimeAndZone (dateTimeInfo); + + for (uint32 previewIndex = 0; previewIndex < 2; previewIndex++) + { + + // Skip preview if writing a compresssed main image to save space + // in this example code. + + if (negative->RawJPEGImage () != NULL && previewIndex > 0) + { + break; + } + + // Report timing. + + dng_timer timer (previewIndex == 0 ? "Build thumbnail time" + : "Build preview time"); + + // Render a preview sized image. + + AutoPtr previewImage; + + { + + dng_render render (host, *negative); + + render.SetFinalSpace (negative->IsMonochrome () ? dng_space_GrayGamma22::Get () + : dng_space_sRGB ::Get ()); + + render.SetFinalPixelType (ttByte); + + render.SetMaximumSize (previewIndex == 0 ? 256 : 1024); + + previewImage.Reset (render.Render ()); + + } + + // Don't write the preview if it is same size as thumbnail. + + if (previewIndex > 0 && + Max_uint32 (previewImage->Bounds ().W (), + previewImage->Bounds ().H ()) <= 256) + { + break; + } + + // If we have compressed JPEG data, create a compressed thumbnail. Otherwise + // save a uncompressed thumbnail. + + bool useCompressedPreview = (negative->RawJPEGImage () != NULL) || + (previewIndex > 0); + + AutoPtr preview (useCompressedPreview ? + (dng_preview *) new dng_jpeg_preview : + (dng_preview *) new dng_image_preview); + + // Setup up preview info. + + preview->fInfo.fApplicationName .Set ("dng_validate"); + preview->fInfo.fApplicationVersion.Set (kDNGValidateVersion); + + preview->fInfo.fSettingsName.Set ("Default"); + + preview->fInfo.fColorSpace = previewImage->Planes () == 1 ? + previewColorSpace_GrayGamma22 : + previewColorSpace_sRGB; + + preview->fInfo.fDateTime = dateTimeInfo.Encode_ISO_8601 (); + + if (!useCompressedPreview) + { + + dng_image_preview *imagePreview = dynamic_cast (preview.Get ()); + + imagePreview->fImage.Reset (previewImage.Release ()); + + } + + else + { + + dng_jpeg_preview *jpegPreview = dynamic_cast (preview.Get ()); + + int32 quality = (previewIndex == 0 ? 8 : 5); + + dng_image_writer writer; + + writer.EncodeJPEGPreview (host, + *previewImage, + *jpegPreview, + quality); + + } + + previewList.Append (preview); + + } + + // Write DNG file. + + dng_file_stream stream2 (gDumpDNG.Get (), true); + + { + + dng_timer timer ("Write DNG time"); + + dng_image_writer writer; + + writer.WriteDNG (host, + stream2, + *negative.Get (), + &previewList, + dngVersion_Current, + false); + + } + + gDumpDNG.Clear (); + + } + + // Output TIF file if requested. + + if (gDumpTIF.NotEmpty ()) + { + + // Render final image. + + dng_render render (host, *negative); + + render.SetFinalSpace (*gFinalSpace ); + render.SetFinalPixelType (gFinalPixelType); + + if (host.MinimumSize ()) + { + + dng_point stage3Size = negative->Stage3Image ()->Size (); + + render.SetMaximumSize (Max_uint32 (stage3Size.v, + stage3Size.h)); + + } + + AutoPtr finalImage; + + { + + dng_timer timer ("Render time"); + + finalImage.Reset (render.Render ()); + + } + + finalImage->Rotate (negative->Orientation ()); + + // Now that Camera Raw supports non-raw formats, we should + // not keep any Camera Raw settings in the XMP around when + // writing rendered files. + + if (negative->GetXMP ()) + { + + negative->GetXMP ()->RemoveProperties (XMP_NS_CRS); + negative->GetXMP ()->RemoveProperties (XMP_NS_CRSS); + + } + + // Write TIF file. + + dng_file_stream stream2 (gDumpTIF.Get (), true); + + { + + dng_timer timer ("Write TIFF time"); + + dng_image_writer writer; + + writer.WriteTIFF (host, + stream2, + *finalImage.Get (), + finalImage->Planes () >= 3 ? piRGB + : piBlackIsZero, + ccUncompressed, + negative.Get (), + &render.FinalSpace ()); + + } + + gDumpTIF.Clear (); + + } + + } + + catch (const dng_exception &except) + { + + return except.ErrorCode (); + + } + + catch (...) + { + + return dng_error_unknown; + + } + + printf ("Validation complete\n"); + + return dng_error_none; + + } + +/*****************************************************************************/ + +int main (int argc, char *argv []) + { + + try + { + + if (argc == 1) + { + + fprintf (stderr, + "\n" + "dng_validate, version " kDNGValidateVersion " " + #if qDNG64Bit + "(64-bit)" + #else + "(32-bit)" + #endif + "\n" + "Copyright 2005-2012 Adobe Systems, Inc.\n" + "\n" + "Usage: %s [options] file1 file2 ...\n" + "\n" + "Valid options:\n" + "-v Verbose mode\n" + "-d Dump line limit (implies -v)\n" + "-b4 Use four-color Bayer interpolation\n" + "-s Use this sample of multi-sample CFAs\n" + "-size Preferred preview image size\n" + "-min Minimum preview image size\n" + "-max Maximum preview image size\n" + "-proxy Target size for proxy DNG\n" + "-cs1 Color space: \"sRGB\" (default)\n" + "-cs2 Color space: \"Adobe RGB\"\n" + "-cs3 Color space: \"ProPhoto RGB\"\n" + "-cs4 Color space: \"ColorMatch RGB\"\n" + "-cs5 Color space: \"Gray Gamma 1.8\"\n" + "-cs6 Color space: \"Gray Gamma 2.2\"\n" + "-16 16-bits/channel output\n" + "-1 Write stage 1 image to \".tif\"\n" + "-2 Write stage 2 image to \".tif\"\n" + "-3 Write stage 3 image to \".tif\"\n" + "-tif Write TIF image to \".tif\"\n" + "-dng Write DNG image to \".dng\"\n" + "\n", + argv [0]); + + return 1; + + } + + int index; + + for (index = 1; index < argc && argv [index] [0] == '-'; index++) + { + + dng_string option; + + option.Set (&argv [index] [1]); + + if (option.Matches ("v", true)) + { + gVerbose = true; + } + + else if (option.Matches ("d", true)) + { + + gVerbose = true; + + gDumpLineLimit = 0; + + if (index + 1 < argc) + { + gDumpLineLimit = atoi (argv [++index]); + } + + if (!gDumpLineLimit) + { + fprintf (stderr, "*** Invalid number after -d\n"); + return 1; + } + + } + + else if (option.Matches ("s", true)) + { + + if (index + 1 < argc) + { + gMosaicPlane = atoi (argv [++index]); + } + + else + { + fprintf (stderr, "*** Missing number after -s\n"); + return 1; + } + + } + + else if (option.Matches ("b4", true)) + { + gFourColorBayer = true; + } + + else if (option.Matches ("size", true)) + { + + if (index + 1 < argc) + { + gPreferredSize = (uint32) atoi (argv [++index]); + } + + else + { + fprintf (stderr, "*** Missing number after -size\n"); + return 1; + } + + } + + else if (option.Matches ("min", true)) + { + + if (index + 1 < argc) + { + gMinimumSize = (uint32) atoi (argv [++index]); + } + + else + { + fprintf (stderr, "*** Missing number after -min\n"); + return 1; + } + + } + + else if (option.Matches ("max", true)) + { + + if (index + 1 < argc) + { + gMaximumSize = (uint32) atoi (argv [++index]); + } + + else + { + fprintf (stderr, "*** Missing number after -max\n"); + return 1; + } + + } + + else if (option.Matches ("proxy", true)) + { + + if (index + 1 < argc) + { + gProxyDNGSize = (uint32) atoi (argv [++index]); + } + + else + { + fprintf (stderr, "*** Missing number after -proxy\n"); + return 1; + } + + } + + else if (option.Matches ("cs1", true)) + { + + gFinalSpace = &dng_space_sRGB::Get (); + + } + + else if (option.Matches ("cs2", true)) + { + + gFinalSpace = &dng_space_AdobeRGB::Get (); + + } + + else if (option.Matches ("cs3", true)) + { + + gFinalSpace = &dng_space_ProPhoto::Get (); + + } + + else if (option.Matches ("cs4", true)) + { + + gFinalSpace = &dng_space_ColorMatch::Get (); + + } + + else if (option.Matches ("cs5", true)) + { + + gFinalSpace = &dng_space_GrayGamma18::Get (); + + } + + else if (option.Matches ("cs6", true)) + { + + gFinalSpace = &dng_space_GrayGamma22::Get (); + + } + + else if (option.Matches ("16")) + { + + gFinalPixelType = ttShort; + + } + + else if (option.Matches ("1")) + { + + gDumpStage1.Clear (); + + if (index + 1 < argc) + { + gDumpStage1.Set (argv [++index]); + } + + if (gDumpStage1.IsEmpty () || gDumpStage1.StartsWith ("-")) + { + fprintf (stderr, "*** Missing file name after -1\n"); + return 1; + } + + if (!gDumpStage1.EndsWith (".tif")) + { + gDumpStage1.Append (".tif"); + } + + } + + else if (option.Matches ("2")) + { + + gDumpStage2.Clear (); + + if (index + 1 < argc) + { + gDumpStage2.Set (argv [++index]); + } + + if (gDumpStage2.IsEmpty () || gDumpStage2.StartsWith ("-")) + { + fprintf (stderr, "*** Missing file name after -2\n"); + return 1; + } + + if (!gDumpStage2.EndsWith (".tif")) + { + gDumpStage2.Append (".tif"); + } + + } + + else if (option.Matches ("3")) + { + + gDumpStage3.Clear (); + + if (index + 1 < argc) + { + gDumpStage3.Set (argv [++index]); + } + + if (gDumpStage3.IsEmpty () || gDumpStage3.StartsWith ("-")) + { + fprintf (stderr, "*** Missing file name after -3\n"); + return 1; + } + + if (!gDumpStage3.EndsWith (".tif")) + { + gDumpStage3.Append (".tif"); + } + + } + + else if (option.Matches ("tif", true)) + { + + gDumpTIF.Clear (); + + if (index + 1 < argc) + { + gDumpTIF.Set (argv [++index]); + } + + if (gDumpTIF.IsEmpty () || gDumpTIF.StartsWith ("-")) + { + fprintf (stderr, "*** Missing file name after -tif\n"); + return 1; + } + + if (!gDumpTIF.EndsWith (".tif")) + { + gDumpTIF.Append (".tif"); + } + + } + + else if (option.Matches ("dng", true)) + { + + gDumpDNG.Clear (); + + if (index + 1 < argc) + { + gDumpDNG.Set (argv [++index]); + } + + if (gDumpDNG.IsEmpty () || gDumpDNG.StartsWith ("-")) + { + fprintf (stderr, "*** Missing file name after -dng\n"); + return 1; + } + + if (!gDumpDNG.EndsWith (".dng")) + { + gDumpDNG.Append (".dng"); + } + + } + + else + { + fprintf (stderr, "*** Unknown option \"-%s\"\n", option.Get ()); + return 1; + } + + } + + if (index == argc) + { + fprintf (stderr, "*** No file specified\n"); + return 1; + } + + dng_xmp_sdk::InitializeSDK (); + + int result = 0; + + while (index < argc) + { + + if (dng_validate (argv [index++]) != dng_error_none) + { + + result = 1; + + } + + } + + dng_xmp_sdk::TerminateSDK (); + + return result; + + } + + catch (...) + { + + } + + fprintf (stderr, "*** Exception thrown in main routine\n"); + + return 1; + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_xmp.cpp b/source/lib/dng_sdk/dng_xmp.cpp new file mode 100644 index 0000000..13ba6c3 --- /dev/null +++ b/source/lib/dng_sdk/dng_xmp.cpp @@ -0,0 +1,4417 @@ +/*****************************************************************************/ +// Copyright 2006-2008 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_xmp.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_xmp.h" + +#include "dng_assertions.h" +#include "dng_date_time.h" +#include "dng_exceptions.h" +#include "dng_exif.h" +#include "dng_image_writer.h" +#include "dng_iptc.h" +#include "dng_negative.h" +#include "dng_string.h" +#include "dng_string_list.h" +#include "dng_utils.h" +#include "dng_xmp_sdk.h" + +#include "stdc_includes.h" + +/*****************************************************************************/ + +dng_xmp::dng_xmp (dng_memory_allocator &allocator) + + : fAllocator (allocator) + + , fSDK (NULL) + + { + + fSDK = new dng_xmp_sdk (); + + if (!fSDK) + { + ThrowMemoryFull (); + } + + } + +/*****************************************************************************/ + +dng_xmp::dng_xmp (const dng_xmp &xmp) + + : fAllocator (xmp.fAllocator) + + , fSDK (NULL) + + { + + fSDK = new dng_xmp_sdk (*xmp.fSDK); + + if (!fSDK) + { + ThrowMemoryFull (); + } + + } + +/*****************************************************************************/ + +dng_xmp::~dng_xmp () + { + + if (fSDK) + { + + delete fSDK; + + } + + } + +/*****************************************************************************/ + +dng_xmp * dng_xmp::Clone () const + { + + dng_xmp *result = new dng_xmp (*this); + + if (!result) + { + ThrowMemoryFull (); + } + + return result; + + } + +/*****************************************************************************/ + +void dng_xmp::TrimDecimal (char *s) + { + + uint32 len = (uint32) strlen (s); + + while (len > 0) + { + + if (s [len - 1] == '0') + s [--len] = 0; + + else + break; + + } + + if (len > 0) + { + + if (s [len - 1] == '.') + s [--len] = 0; + + } + + } + +/*****************************************************************************/ + +dng_string dng_xmp::EncodeFingerprint (const dng_fingerprint &f, + bool allowInvalid) + { + + dng_string result; + + if (f.IsValid () || allowInvalid) + { + + char s [dng_fingerprint::kDNGFingerprintSize * 2 + 1]; + + f.ToUtf8HexString (s); + + result.Set (s); + + } + + return result; + + } + +/*****************************************************************************/ + +dng_fingerprint dng_xmp::DecodeFingerprint (const dng_string &s) + { + + dng_fingerprint result; + + if (s.Length () == 32) + result.FromUtf8HexString (s.Get ()); + + return result; + + } + +/*****************************************************************************/ + +dng_string dng_xmp::EncodeGPSVersion (uint32 version) + { + + dng_string result; + + if (version) + { + + uint8 b0 = (uint8) (version >> 24); + uint8 b1 = (uint8) (version >> 16); + uint8 b2 = (uint8) (version >> 8); + uint8 b3 = (uint8) (version ); + + if (b0 <= 9 && b1 <= 9 && b2 <= 9 && b3 <= 9) + { + + char s [32]; + + sprintf (s, + "%u.%u.%u.%u", + (unsigned) b0, + (unsigned) b1, + (unsigned) b2, + (unsigned) b3); + + result.Set (s); + + } + + } + + return result; + + } + +/*****************************************************************************/ + +uint32 dng_xmp::DecodeGPSVersion (const dng_string &s) + { + + uint32 result = 0; + + if (s.Length () == 7) + { + + unsigned b0 = 0; + unsigned b1 = 0; + unsigned b2 = 0; + unsigned b3 = 0; + + if (sscanf (s.Get (), + "%u.%u.%u.%u", + &b0, + &b1, + &b2, + &b3) == 4) + { + + result = (b0 << 24) | + (b1 << 16) | + (b2 << 8) | + (b3 ); + + } + + } + + return result; + + } + +/*****************************************************************************/ + +dng_string dng_xmp::EncodeGPSCoordinate (const dng_string &ref, + const dng_urational *coord) + { + + dng_string result; + + if (ref.Length () == 1 && coord [0].IsValid () && + coord [1].IsValid ()) + { + + char refChar = ForceUppercase (ref.Get () [0]); + + if (refChar == 'N' || + refChar == 'S' || + refChar == 'E' || + refChar == 'W') + { + + char s [256]; + + // Use the seconds case if all three values are + // integers. + + if (coord [0].d == 1 && + coord [1].d == 1 && + coord [2].d == 1) + { + + sprintf (s, + "%u,%u,%u%c", + (unsigned) coord [0].n, + (unsigned) coord [1].n, + (unsigned) coord [2].n, + refChar); + + } + + // Else we need to use the fractional minutes case. + + else + { + + // Find value minutes. + + real64 x = coord [0].As_real64 () * 60.0 + + coord [1].As_real64 () + + coord [2].As_real64 () * (1.0 / 60.0); + + // Round to fractional four decimal places. + + uint32 y = Round_uint32 (x * 10000.0); + + // Split into degrees and minutes. + + uint32 d = y / (60 * 10000); + uint32 m = y % (60 * 10000); + + char min [32]; + + sprintf (min, "%.4f", m * (1.0 / 10000.0)); + + TrimDecimal (min); + + sprintf (s, + "%u,%s%c", + (unsigned) d, + min, + refChar); + + } + + result.Set (s); + + } + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_xmp::DecodeGPSCoordinate (const dng_string &s, + dng_string &ref, + dng_urational *coord) + { + + ref.Clear (); + + coord [0].Clear (); + coord [1].Clear (); + coord [2].Clear (); + + if (s.Length () > 1) + { + + char refChar = ForceUppercase (s.Get () [s.Length () - 1]); + + if (refChar == 'N' || + refChar == 'S' || + refChar == 'E' || + refChar == 'W') + { + + dng_string ss (s); + + ss.Truncate (ss.Length () - 1); + + ss.NormalizeAsCommaSeparatedNumbers(); + + int degrees = 0; + + real64 minutes = 0.0; + real64 seconds = 0.0; + + int count = sscanf (ss.Get (), + "%d,%lf,%lf", + °rees, + &minutes, + &seconds); + + if (count < 1) + { + return; + } + + // The degree, minute, second values should always be positive. + + if (degrees < 0 || minutes < 0 || seconds < 0) + { + return; + } + + coord [0] = dng_urational ((uint32) degrees, 1); + + if (count <= 2) + { + coord [1].Set_real64 (minutes, 10000); + coord [2] = dng_urational (0, 1); + } + else + { + coord [1].Set_real64 (minutes, 1); + coord [2].Set_real64 (seconds, 100); + } + + char r [2]; + + r [0] = refChar; + r [1] = 0; + + ref.Set (r); + + } + + } + + } + +/*****************************************************************************/ + +dng_string dng_xmp::EncodeGPSDateTime (const dng_string &dateStamp, + const dng_urational *timeStamp) + { + + dng_string result; + + if (timeStamp [0].IsValid () && + timeStamp [1].IsValid () && + timeStamp [2].IsValid ()) + { + + char s [256]; + + char sec [32]; + + sprintf (sec, + "%09.6f", + timeStamp [2].As_real64 ()); + + TrimDecimal (sec); + + int year = 0; + int month = 0; + int day = 0; + + if (dateStamp.NotEmpty ()) + { + + sscanf (dateStamp.Get (), + "%d:%d:%d", + &year, + &month, + &day); + + } + + if (year >= 1 && year <= 9999 && + month >= 1 && month <= 12 && + day >= 1 && day <= 31) + { + + sprintf (s, + "%04d-%02d-%02dT%02u:%02u:%sZ", + year, + month, + day, + (unsigned) Round_uint32 (timeStamp [0].As_real64 ()), + (unsigned) Round_uint32 (timeStamp [1].As_real64 ()), + sec); + + } + + else + { + + sprintf (s, + "%02u:%02u:%sZ", + (unsigned) Round_uint32 (timeStamp [0].As_real64 ()), + (unsigned) Round_uint32 (timeStamp [1].As_real64 ()), + sec); + + } + + result.Set (s); + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_xmp::DecodeGPSDateTime (const dng_string &s, + dng_string &dateStamp, + dng_urational *timeStamp) + { + + dateStamp.Clear (); + + timeStamp [0].Clear (); + timeStamp [1].Clear (); + timeStamp [2].Clear (); + + if (s.NotEmpty ()) + { + + unsigned year = 0; + unsigned month = 0; + unsigned day = 0; + unsigned hour = 0; + unsigned minute = 0; + + double second = 0.0; + + if (sscanf (s.Get (), + "%u-%u-%uT%u:%u:%lf", + &year, + &month, + &day, + &hour, + &minute, + &second) == 6) + { + + if (year >= 1 && year <= 9999 && + month >= 1 && month <= 12 && + day >= 1 && day <= 31 ) + { + + char ss [64]; + + sprintf (ss, + "%04u:%02u:%02u", + year, + month, + day); + + dateStamp.Set (ss); + + } + + } + + else if (sscanf (s.Get (), + "%u:%u:%lf", + &hour, + &minute, + &second) != 3) + { + + return; + + } + + timeStamp [0] = dng_urational ((uint32) hour , 1); + timeStamp [1] = dng_urational ((uint32) minute, 1); + + timeStamp [2].Set_real64 (second, 1000); + + } + + } + +/*****************************************************************************/ + +void dng_xmp::Parse (dng_host &host, + const void *buffer, + uint32 count) + { + + fSDK->Parse (host, + (const char *) buffer, + count); + + } + +/*****************************************************************************/ + +dng_memory_block * dng_xmp::Serialize (bool asPacket, + uint32 targetBytes, + uint32 padBytes, + bool forJPEG, + bool compact) const + { + + return fSDK->Serialize (fAllocator, + asPacket, + targetBytes, + padBytes, + forJPEG, + compact); + + } + +/*****************************************************************************/ + +void dng_xmp::PackageForJPEG (AutoPtr &stdBlock, + AutoPtr &extBlock, + dng_string &extDigest) const + { + + fSDK->PackageForJPEG (fAllocator, + stdBlock, + extBlock, + extDigest); + + } + +/*****************************************************************************/ + +void dng_xmp::MergeFromJPEG (const dng_xmp &xmp) + { + + fSDK->MergeFromJPEG (xmp.fSDK); + + } + +/*****************************************************************************/ + +bool dng_xmp::HasMeta () const + { + + return fSDK->HasMeta (); + + } + +/*****************************************************************************/ + +void * dng_xmp::GetPrivateMeta () + { + + return fSDK->GetPrivateMeta (); + + } + +/*****************************************************************************/ + +bool dng_xmp::Exists (const char *ns, + const char *path) const + { + + return fSDK->Exists (ns, path); + + } + +/*****************************************************************************/ + +bool dng_xmp::HasNameSpace (const char *ns) const + { + + return fSDK->HasNameSpace (ns); + + } + +/*****************************************************************************/ + +bool dng_xmp::IteratePaths (IteratePathsCallback *callback, + void *callbackData, + const char *ns, + const char *path) + { + + return fSDK->IteratePaths (callback, callbackData, ns, path); + + } + +/*****************************************************************************/ + +void dng_xmp::Remove (const char *ns, + const char *path) + { + + fSDK->Remove (ns, path); + + } + +/*****************************************************************************/ + +void dng_xmp::RemoveProperties (const char *ns) + { + + fSDK->RemoveProperties (ns); + + } + +/*****************************************************************************/ + +void dng_xmp::RemoveEmptyStringOrArray (const char *ns, + const char *path) + { + + if (path == NULL || path [0] == 0) + { + return; + } + + if (fSDK->IsEmptyString (ns, path) || + fSDK->IsEmptyArray (ns, path)) + { + + Remove (ns, path); + + } + + } + +/*****************************************************************************/ + +static bool RemoveEmptyStringsAndArraysCallback (const char *ns, + const char *path, + void *callbackData) + { + + dng_xmp *xmp = (dng_xmp *) callbackData; + + xmp->RemoveEmptyStringOrArray (ns, path); + + return true; + + } + +/*****************************************************************************/ + +void dng_xmp::RemoveEmptyStringsAndArrays (const char *ns) + { + + IteratePaths (RemoveEmptyStringsAndArraysCallback, + (void *) this, + ns, + NULL); + + } + +/*****************************************************************************/ + +void dng_xmp::Set (const char *ns, + const char *path, + const char *text) + { + + fSDK->Set (ns, path, text); + + } + +/*****************************************************************************/ + +bool dng_xmp::GetString (const char *ns, + const char *path, + dng_string &s) const + { + + return fSDK->GetString (ns, path, s); + + } + +/*****************************************************************************/ + +void dng_xmp::SetString (const char *ns, + const char *path, + const dng_string &s) + { + + fSDK->SetString (ns, path, s); + + } + +/*****************************************************************************/ + +bool dng_xmp::SyncString (const char *ns, + const char *path, + dng_string &s, + uint32 options) + { + + bool isDefault = s.IsEmpty (); + + // Sync 1: Force XMP to match non-XMP. + + if (options & ignoreXMP) + { + + if (isDefault || (options & removeXMP)) + { + + Remove (ns, path); + + } + + else + { + + SetString (ns, path, s); + + } + + return false; + + } + + // Sync 2: From non-XMP to XMP if non-XMP is prefered. + + if ((options & preferNonXMP) && !isDefault) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else + { + + SetString (ns, path, s); + + } + + return false; + + } + + // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. + + if ((options & preferXMP) || isDefault) + { + + if (GetString (ns, path, s)) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + return true; + + } + + } + + // Sync 4: From non-XMP to XMP. + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else if (!isDefault) + { + + SetString (ns, path, s); + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_xmp::GetStringList (const char *ns, + const char *path, + dng_string_list &list) const + { + + return fSDK->GetStringList (ns, path, list); + + } + +/*****************************************************************************/ + +void dng_xmp::SetStringList (const char *ns, + const char *path, + const dng_string_list &list, + bool isBag) + { + + fSDK->SetStringList (ns, path, list, isBag); + + } + +/*****************************************************************************/ + +void dng_xmp::SyncStringList (const char *ns, + const char *path, + dng_string_list &list, + bool isBag, + uint32 options) + { + + bool isDefault = (list.Count () == 0); + + // First make sure the XMP is not badly formatted, since + // this breaks some Photoshop logic. + + ValidateStringList (ns, path); + + // Sync 1: Force XMP to match non-XMP. + + if (options & ignoreXMP) + { + + if (isDefault) + { + + Remove (ns, path); + + } + + else + { + + SetStringList (ns, path, list, isBag); + + } + + return; + + } + + // Sync 2: From non-XMP to XMP if non-XMP is prefered. + + if ((options & preferNonXMP) && !isDefault) + { + + SetStringList (ns, path, list, isBag); + + return; + + } + + // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. + + if ((options & preferXMP) || isDefault) + { + + if (GetStringList (ns, path, list)) + { + + return; + + } + + } + + // Sync 4: From non-XMP to XMP. + + if (!isDefault) + { + + SetStringList (ns, path, list, isBag); + + } + + } + +/*****************************************************************************/ + +void dng_xmp::SetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + const dng_string &s) + { + + dng_string ss (s); + + ss.SetLineEndings ('\n'); + + ss.StripLowASCII (); + + fSDK->SetStructField (ns, path, fieldNS, fieldName, ss.Get ()); + + } + +/*****************************************************************************/ + +void dng_xmp::SetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + const char *s) + { + + fSDK->SetStructField (ns, path, fieldNS, fieldName, s); + + } + +/*****************************************************************************/ + +void dng_xmp::DeleteStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName) + { + + fSDK->DeleteStructField (ns, path, fieldNS, fieldName); + + } + +/*****************************************************************************/ + +bool dng_xmp::GetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + dng_string &s) const + { + + return fSDK->GetStructField (ns, path, fieldNS, fieldName, s); + + } + +/*****************************************************************************/ + +void dng_xmp::SetAltLangDefault (const char *ns, + const char *path, + const dng_string &s) + { + + fSDK->SetAltLangDefault (ns, path, s); + + } + +/*****************************************************************************/ + +bool dng_xmp::GetAltLangDefault (const char *ns, + const char *path, + dng_string &s) const + { + + return fSDK->GetAltLangDefault (ns, path, s); + + } + +/*****************************************************************************/ + +bool dng_xmp::SyncAltLangDefault (const char *ns, + const char *path, + dng_string &s, + uint32 options) + { + + bool isDefault = s.IsEmpty (); + + // Sync 1: Force XMP to match non-XMP. + + if (options & ignoreXMP) + { + + if (isDefault) + { + + Remove (ns, path); + + } + + else + { + + SetAltLangDefault (ns, path, s); + + } + + return false; + + } + + // Sync 2: From non-XMP to XMP if non-XMP is prefered. + + if ((options & preferNonXMP) && !isDefault) + { + + SetAltLangDefault (ns, path, s); + + return false; + + } + + // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. + + if ((options & preferXMP) || isDefault) + { + + if (GetAltLangDefault (ns, path, s)) + { + + return true; + + } + + } + + // Sync 4: From non-XMP to XMP. + + if (!isDefault) + { + + SetAltLangDefault (ns, path, s); + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_xmp::GetBoolean (const char *ns, + const char *path, + bool &x) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + if (s.Matches ("True")) + { + + x = true; + + return true; + + } + + if (s.Matches ("False")) + { + + x = false; + + return true; + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp::SetBoolean (const char *ns, + const char *path, + bool x) + { + + Set (ns, path, x ? "True" : "False"); + + } + +/*****************************************************************************/ + +bool dng_xmp::Get_int32 (const char *ns, + const char *path, + int32 &x) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + int y = 0; + + if (sscanf (s.Get (), "%d", &y) == 1) + { + + x = y; + + return true; + + } + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp::Set_int32 (const char *ns, + const char *path, + int32 x, + bool usePlus) + { + + char s [64]; + + if (x > 0 && usePlus) + { + sprintf (s, "+%d", (int) x); + } + else + { + sprintf (s, "%d", (int) x); + } + + Set (ns, path, s); + + } + +/*****************************************************************************/ + +bool dng_xmp::Get_uint32 (const char *ns, + const char *path, + uint32 &x) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + unsigned y = 0; + + if (sscanf (s.Get (), "%u", &y) == 1) + { + + x = y; + + return true; + + } + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp::Set_uint32 (const char *ns, + const char *path, + uint32 x) + { + + char s [64]; + + sprintf (s, + "%u", + (unsigned) x); + + Set (ns, path, s); + + } + +/*****************************************************************************/ + +void dng_xmp::Sync_uint32 (const char *ns, + const char *path, + uint32 &x, + bool isDefault, + uint32 options) + { + + // Sync 1: Force XMP to match non-XMP. + + if (options & ignoreXMP) + { + + if (isDefault || (options & removeXMP)) + { + + Remove (ns, path); + + } + + else + { + + Set_uint32 (ns, path, x); + + } + + return; + + } + + // Sync 2: From non-XMP to XMP if non-XMP is prefered. + + if ((options & preferNonXMP) && !isDefault) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else + { + + Set_uint32 (ns, path, x); + + } + + return; + + } + + // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. + + if ((options & preferXMP) || isDefault) + { + + if (Get_uint32 (ns, path, x)) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + return; + + } + + } + + // Sync 4: From non-XMP to XMP. + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else if (!isDefault) + { + + Set_uint32 (ns, path, x); + + } + + } + +/*****************************************************************************/ + +void dng_xmp::Sync_uint32_array (const char *ns, + const char *path, + uint32 *data, + uint32 &count, + uint32 maxCount, + uint32 options) + { + + dng_string_list list; + + for (uint32 j = 0; j < count; j++) + { + + char s [32]; + + sprintf (s, "%u", (unsigned) data [j]); + + dng_string ss; + + ss.Set (s); + + list.Append (ss); + + } + + SyncStringList (ns, + path, + list, + false, + options); + + count = 0; + + for (uint32 k = 0; k < maxCount; k++) + { + + data [k] = 0; + + if (k < list.Count ()) + { + + unsigned x = 0; + + if (sscanf (list [k].Get (), "%u", &x) == 1) + { + + data [count++] = x; + + } + + } + + } + + } + +/*****************************************************************************/ + +bool dng_xmp::Get_real64 (const char *ns, + const char *path, + real64 &x) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + double y = 0; + + if (sscanf (s.Get (), "%lf", &y) == 1) + { + + x = y; + + return true; + + } + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp::Set_real64 (const char *ns, + const char *path, + real64 x, + uint32 places, + bool trim, + bool usePlus) + { + + char s [64]; + + if (x > 0.0 && usePlus) + { + sprintf (s, "+%0.*f", (unsigned) places, (double) x); + } + else + { + sprintf (s, "%0.*f", (unsigned) places, (double) x); + } + + if (trim) + { + + while (s [strlen (s) - 1] == '0') + { + s [strlen (s) - 1] = 0; + } + + if (s [strlen (s) - 1] == '.') + { + s [strlen (s) - 1] = 0; + } + + } + + Set (ns, path, s); + + } + +/*****************************************************************************/ + +bool dng_xmp::Get_urational (const char *ns, + const char *path, + dng_urational &r) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + unsigned n = 0; + unsigned d = 0; + + if (sscanf (s.Get (), "%u/%u", &n, &d) == 2) + { + + if (d != 0) + { + + r = dng_urational (n, d); + + return true; + + } + + } + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp::Set_urational (const char *ns, + const char *path, + const dng_urational &r) + { + + char s [64]; + + sprintf (s, + "%u/%u", + (unsigned) r.n, + (unsigned) r.d); + + Set (ns, path, s); + + } + +/*****************************************************************************/ + +void dng_xmp::Sync_urational (const char *ns, + const char *path, + dng_urational &r, + uint32 options) + { + + bool isDefault = r.NotValid (); + + // Sync 1: Force XMP to match non-XMP. + + if (options & ignoreXMP) + { + + if (isDefault || (options & removeXMP)) + { + + Remove (ns, path); + + } + + else + { + + Set_urational (ns, path, r); + + } + + return; + + } + + // Sync 2: From non-XMP to XMP if non-XMP is prefered. + + if ((options & preferNonXMP) && !isDefault) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else + { + + Set_urational (ns, path, r); + + } + + return; + + } + + // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. + + if ((options & preferXMP) || isDefault) + { + + if (Get_urational (ns, path, r)) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + return; + + } + + } + + // Sync 4: From non-XMP to XMP. + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else if (!isDefault) + { + + Set_urational (ns, path, r); + + } + + } + +/*****************************************************************************/ + +bool dng_xmp::Get_srational (const char *ns, + const char *path, + dng_srational &r) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + if (s.NotEmpty ()) + { + + int n = 0; + int d = 0; + + if (sscanf (s.Get (), "%d/%d", &n, &d) == 2) + { + + if (d != 0) + { + + r = dng_srational (n, d); + + return true; + + } + + } + + } + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp::Set_srational (const char *ns, + const char *path, + const dng_srational &r) + { + + char s [64]; + + sprintf (s, + "%d/%d", + (int) r.n, + (int) r.d); + + Set (ns, path, s); + + } + +/*****************************************************************************/ + +void dng_xmp::Sync_srational (const char *ns, + const char *path, + dng_srational &r, + uint32 options) + { + + bool isDefault = r.NotValid (); + + // Sync 1: Force XMP to match non-XMP. + + if (options & ignoreXMP) + { + + if (isDefault || (options & removeXMP)) + { + + Remove (ns, path); + + } + + else + { + + Set_srational (ns, path, r); + + } + + return; + + } + + // Sync 2: From non-XMP to XMP if non-XMP is prefered. + + if ((options & preferNonXMP) && !isDefault) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else + { + + Set_srational (ns, path, r); + + } + + return; + + } + + // Sync 3: From XMP to non-XMP if XMP is prefered or default non-XMP. + + if ((options & preferXMP) || isDefault) + { + + if (Get_srational (ns, path, r)) + { + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + return; + + } + + } + + // Sync 4: From non-XMP to XMP. + + if (options & removeXMP) + { + + Remove (ns, path); + + } + + else if (!isDefault) + { + + Set_srational (ns, path, r); + + } + + } + +/*****************************************************************************/ + +bool dng_xmp::GetFingerprint (const char *ns, + const char *path, + dng_fingerprint &print) const + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + dng_fingerprint temp = DecodeFingerprint (s); + + if (temp.IsValid ()) + { + + print = temp; + + return true; + + } + + } + + return false; + + } + +/******************************************************************************/ + +void dng_xmp::SetFingerprint (const char *ns, + const char *tag, + const dng_fingerprint &print, + bool allowInvalid) + { + + dng_string s = EncodeFingerprint (print, allowInvalid); + + if (s.IsEmpty ()) + { + + Remove (ns, tag); + + } + + else + { + + SetString (ns, tag, s); + + } + + } + +/******************************************************************************/ + +void dng_xmp::SetVersion2to4 (const char *ns, + const char *path, + uint32 version) + { + + char buf [32]; + + if (version & 0x000000ff) + { + + // x.x.x.x + + sprintf (buf, + "%u.%u.%u.%u", + (unsigned) ((version >> 24) & 0xff), + (unsigned) ((version >> 16) & 0xff), + (unsigned) ((version >> 8) & 0xff), + (unsigned) ((version ) & 0xff)); + + } + + else if (version & 0x0000ff00) + { + + // x.x.x + + sprintf (buf, + "%u.%u.%u", + (unsigned) ((version >> 24) & 0xff), + (unsigned) ((version >> 16) & 0xff), + (unsigned) ((version >> 8) & 0xff)); + + } + + else + { + + // x.x + + sprintf (buf, + "%u.%u", + (unsigned) ((version >> 24) & 0xff), + (unsigned) ((version >> 16) & 0xff)); + + } + + Set (ns, path, buf); + + } + +/******************************************************************************/ + +dng_fingerprint dng_xmp::GetIPTCDigest () const + { + + dng_fingerprint digest; + + if (GetFingerprint (XMP_NS_PHOTOSHOP, + "LegacyIPTCDigest", + digest)) + { + + return digest; + + } + + return dng_fingerprint (); + + } + +/******************************************************************************/ + +void dng_xmp::SetIPTCDigest (dng_fingerprint &digest) + { + + SetFingerprint (XMP_NS_PHOTOSHOP, + "LegacyIPTCDigest", + digest); + + } + +/******************************************************************************/ + +void dng_xmp::ClearIPTCDigest () + { + + Remove (XMP_NS_PHOTOSHOP, "LegacyIPTCDigest"); + + } + +/*****************************************************************************/ + +void dng_xmp::SyncIPTC (dng_iptc &iptc, + uint32 options) + { + + SyncAltLangDefault (XMP_NS_DC, + "title", + iptc.fTitle, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "Category", + iptc.fCategory, + options); + + { + + uint32 x = 0xFFFFFFFF; + + if (iptc.fUrgency >= 0) + { + + x = (uint32) iptc.fUrgency; + + } + + Sync_uint32 (XMP_NS_PHOTOSHOP, + "Urgency", + x, + x == 0xFFFFFFFF, + options); + + if (x <= 9) + { + + iptc.fUrgency = (int32) x; + + } + + } + + SyncStringList (XMP_NS_PHOTOSHOP, + "SupplementalCategories", + iptc.fSupplementalCategories, + true, + options); + + SyncStringList (XMP_NS_PHOTOSHOP, + "Keywords", + iptc.fKeywords, + true, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "Instructions", + iptc.fInstructions, + options); + + { + + dng_string s = iptc.fDateTimeCreated.Encode_ISO_8601 (); + + if (SyncString (XMP_NS_PHOTOSHOP, + "DateCreated", + s, + options)) + { + + iptc.fDateTimeCreated.Decode_ISO_8601 (s.Get ()); + + } + + } + + { + + dng_string s = iptc.fDigitalCreationDateTime.Encode_ISO_8601 (); + + if (SyncString (XMP_NS_EXIF, + "DateTimeDigitized", + s, + options)) + { + + iptc.fDigitalCreationDateTime.Decode_ISO_8601 (s.Get ()); + + } + + } + + SyncStringList (XMP_NS_DC, + "creator", + iptc.fAuthors, + false, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "AuthorsPosition", + iptc.fAuthorsPosition, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "City", + iptc.fCity, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "State", + iptc.fState, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "Country", + iptc.fCountry, + options); + + SyncString (XMP_NS_IPTC, + "CountryCode", + iptc.fCountryCode, + options); + + SyncString (XMP_NS_IPTC, + "Location", + iptc.fLocation, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "TransmissionReference", + iptc.fTransmissionReference, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "Headline", + iptc.fHeadline, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "Credit", + iptc.fCredit, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "Source", + iptc.fSource, + options); + + SyncAltLangDefault (XMP_NS_DC, + "rights", + iptc.fCopyrightNotice, + options); + + SyncAltLangDefault (XMP_NS_DC, + "description", + iptc.fDescription, + options); + + SyncString (XMP_NS_PHOTOSHOP, + "CaptionWriter", + iptc.fDescriptionWriter, + options); + + } + +/*****************************************************************************/ + +void dng_xmp::IngestIPTC (dng_metadata &metadata, + bool xmpIsNewer) + { + + if (metadata.IPTCLength ()) + { + + // Parse the IPTC block. + + dng_iptc iptc; + + iptc.Parse (metadata.IPTCData (), + metadata.IPTCLength (), + metadata.IPTCOffset ()); + + // Compute fingerprint of IPTC data both ways, including and + // excluding the padding data. + + dng_fingerprint iptcDigest1 = metadata.IPTCDigest (true ); + dng_fingerprint iptcDigest2 = metadata.IPTCDigest (false); + + // See if there is an IPTC fingerprint stored in the XMP. + + dng_fingerprint xmpDigest = GetIPTCDigest (); + + if (xmpDigest.IsValid ()) + { + + // If they match, the XMP was already synced with this + // IPTC block, and we should not resync since it might + // overwrite changes in the XMP data. + + if (iptcDigest1 == xmpDigest) + { + + return; + + } + + // If it matches the incorrectly computed digest, skip + // the sync, but fix the digest in the XMP. + + if (iptcDigest2 == xmpDigest) + { + + SetIPTCDigest (iptcDigest1); + + return; + + } + + // Else the IPTC has changed, so force an update. + + xmpIsNewer = false; + + } + + else + { + + // There is no IPTC digest. Previously we would + // prefer the IPTC in this case, but the MWG suggests + // that we prefer the XMP in this case. + + xmpIsNewer = true; + + } + + // Remember the fingerprint of the IPTC we are syncing with. + + SetIPTCDigest (iptcDigest1); + + // Find the sync options. + + uint32 options = xmpIsNewer ? preferXMP + : preferNonXMP; + + // Synchronize the fields. + + SyncIPTC (iptc, options); + + } + + // After the IPTC data is moved to XMP, we don't need it anymore. + + metadata.ClearIPTC (); + + } + +/*****************************************************************************/ + +void dng_xmp::RebuildIPTC (dng_metadata &metadata, + dng_memory_allocator &allocator, + bool padForTIFF) + { + + // If there is no XMP, then there is no IPTC. + + if (!fSDK->HasMeta ()) + { + return; + } + + // Extract the legacy IPTC fields from the XMP data. + + dng_iptc iptc; + + SyncIPTC (iptc, preferXMP); + + // Build legacy IPTC record + + if (iptc.NotEmpty ()) + { + + AutoPtr block (iptc.Spool (allocator, + padForTIFF)); + + metadata.SetIPTC (block); + + } + + } + +/*****************************************************************************/ + +void dng_xmp::SyncFlash (uint32 &flashState, + uint32 &flashMask, + uint32 options) + { + + bool isDefault = (flashState == 0xFFFFFFFF); + + if ((options & ignoreXMP) || !isDefault) + { + + Remove (XMP_NS_EXIF, "Flash"); + + } + + if (!isDefault) + { + + fSDK->SetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Fired", + (flashState & 0x1) ? "True" : "False"); + + if (((flashMask >> 1) & 3) == 3) + { + + char s [8]; + + sprintf (s, "%u", (unsigned) ((flashState >> 1) & 3)); + + fSDK->SetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Return", + s); + + } + + if (((flashMask >> 3) & 3) == 3) + { + + char s [8]; + + sprintf (s, "%u", (unsigned) ((flashState >> 3) & 3)); + + fSDK->SetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Mode", + s); + + } + + if ((flashMask & (1 << 5)) != 0) + { + + fSDK->SetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Function", + (flashState & (1 << 5)) ? "True" : "False"); + + } + + if ((flashMask & (1 << 6)) != 0) + { + + fSDK->SetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "RedEyeMode", + (flashState & (1 << 6)) ? "True" : "False"); + + } + + } + + else if (fSDK->Exists (XMP_NS_EXIF, "Flash")) + { + + dng_string s; + + if (fSDK->GetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Fired", + s)) + { + + flashState = 0; + flashMask = 1; + + if (s.Matches ("True")) + { + flashState |= 1; + } + + if (fSDK->GetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Return", + s)) + { + + unsigned x = 0; + + if (sscanf (s.Get (), "%u", &x) == 1 && x <= 3) + { + + flashState |= x << 1; + flashMask |= 3 << 1; + + } + + } + + if (fSDK->GetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Mode", + s)) + { + + unsigned x = 0; + + if (sscanf (s.Get (), "%u", &x) == 1 && x <= 3) + { + + flashState |= x << 3; + flashMask |= 3 << 3; + + } + + } + + if (fSDK->GetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "Function", + s)) + { + + flashMask |= 1 << 5; + + if (s.Matches ("True")) + { + flashState |= 1 << 5; + } + + } + + if (fSDK->GetStructField (XMP_NS_EXIF, + "Flash", + XMP_NS_EXIF, + "RedEyeMode", + s)) + { + + flashMask |= 1 << 6; + + if (s.Matches ("True")) + { + flashState |= 1 << 6; + } + + } + + } + + } + + } + +/*****************************************************************************/ + +void dng_xmp::SyncExif (dng_exif &exif, + const dng_exif *originalExif, + bool doingUpdateFromXMP, + bool removeFromXMP) + { + + DNG_ASSERT (!doingUpdateFromXMP || originalExif, + "Must have original EXIF if doingUpdateFromXMP"); + + // Default synchronization options for the read-only fields. + + uint32 readOnly = doingUpdateFromXMP ? ignoreXMP + : preferNonXMP; + + // Option for removable fields. + + uint32 removable = removeFromXMP ? removeXMP + : 0; + + // Make: + + SyncString (XMP_NS_TIFF, + "Make", + exif.fMake, + readOnly + removable); + + // Model: + + SyncString (XMP_NS_TIFF, + "Model", + exif.fModel, + readOnly + removable); + + // Exif version number: + + { + + dng_string exifVersion; + + if (exif.fExifVersion) + { + + unsigned b0 = ((exif.fExifVersion >> 24) & 0x0FF) - '0'; + unsigned b1 = ((exif.fExifVersion >> 16) & 0x0FF) - '0'; + unsigned b2 = ((exif.fExifVersion >> 8) & 0x0FF) - '0'; + unsigned b3 = ((exif.fExifVersion ) & 0x0FF) - '0'; + + if (b0 <= 9 && b1 <= 9 && b2 <= 9 && b3 <= 9) + { + + char s [5]; + + sprintf (s, + "%1u%1u%1u%1u", + b0, + b1, + b2, + b3); + + exifVersion.Set (s); + + } + + } + + SyncString (XMP_NS_EXIF, + "ExifVersion", + exifVersion, + readOnly); + + if (exifVersion.NotEmpty ()) + { + + unsigned b0; + unsigned b1; + unsigned b2; + unsigned b3; + + if (sscanf (exifVersion.Get (), + "%1u%1u%1u%1u", + &b0, + &b1, + &b2, + &b3) == 4) + { + + if (b0 <= 9 && b1 <= 9 && b2 <= 9 && b3 <= 9) + { + + b0 += '0'; + b1 += '0'; + b2 += '0'; + b3 += '0'; + + exif.fExifVersion = (b0 << 24) | + (b1 << 16) | + (b2 << 8) | + (b3 ); + + } + + } + + } + + // Provide default value for ExifVersion. + + if (!exif.fExifVersion) + { + + exif.fExifVersion = DNG_CHAR4 ('0','2','2','1'); + + Set (XMP_NS_EXIF, + "ExifVersion", + "0221"); + + } + + if (removeFromXMP) + { + + Remove (XMP_NS_EXIF, "ExifVersion"); + + } + + } + + // ExposureTime / ShutterSpeedValue: + + { + + // Process twice in case XMP contains only one of the + // two fields. + + for (uint32 pass = 0; pass < 2; pass++) + { + + dng_urational et = exif.fExposureTime; + + Sync_urational (XMP_NS_EXIF, + "ExposureTime", + et, + readOnly); + + if (et.IsValid ()) + { + + exif.SetExposureTime (et.As_real64 (), false); + + } + + dng_srational ss = exif.fShutterSpeedValue; + + Sync_srational (XMP_NS_EXIF, + "ShutterSpeedValue", + ss, + readOnly); + + if (ss.IsValid ()) + { + + exif.SetShutterSpeedValue (ss.As_real64 ()); + + } + + } + + if (removeFromXMP) + { + + Remove (XMP_NS_EXIF, "ExposureTime"); + + Remove (XMP_NS_EXIF, "ShutterSpeedValue"); + + } + + } + + // FNumber / ApertureValue: + + { + + for (uint32 pass = 0; pass < 2; pass++) + { + + dng_urational fs = exif.fFNumber; + + Sync_urational (XMP_NS_EXIF, + "FNumber", + fs, + readOnly); + + if (fs.IsValid ()) + { + + exif.SetFNumber (fs.As_real64 ()); + + } + + dng_urational av = exif.fApertureValue; + + Sync_urational (XMP_NS_EXIF, + "ApertureValue", + av, + readOnly); + + if (av.IsValid ()) + { + + exif.SetApertureValue (av.As_real64 ()); + + } + + } + + if (removeFromXMP) + { + + Remove (XMP_NS_EXIF, "FNumber"); + + Remove (XMP_NS_EXIF, "ApertureValue"); + + } + + } + + // Exposure program: + + Sync_uint32 (XMP_NS_EXIF, + "ExposureProgram", + exif.fExposureProgram, + exif.fExposureProgram == 0xFFFFFFFF, + readOnly + removable); + + // ISO Speed Ratings: + + { + + uint32 isoSpeedRatingsCount = 0; + + uint32 isoSpeedRatingsOptions = readOnly; + + uint32 oldISOSpeedRatings [3]; + + memcpy (oldISOSpeedRatings, + exif.fISOSpeedRatings, + sizeof (oldISOSpeedRatings)); + + bool checkXMPForHigherISO = false; + + for (uint32 j = 0; j < 3; j++) + { + + // Special case: the EXIF 2.2x standard represents ISO speed ratings with + // 2 bytes, which cannot hold ISO speed ratings above 65535 (e.g., + // 102400). If the EXIF ISO speed rating value is 65535, prefer the XMP + // ISOSpeedRatings tag value. + + if (exif.fISOSpeedRatings [j] == 65535) + { + + isoSpeedRatingsOptions = preferXMP; + + checkXMPForHigherISO = true; + + isoSpeedRatingsCount = 0; + + break; + + } + + else if (exif.fISOSpeedRatings [j] == 0) + { + break; + } + + isoSpeedRatingsCount++; + + } + + Sync_uint32_array (XMP_NS_EXIF, + "ISOSpeedRatings", + exif.fISOSpeedRatings, + isoSpeedRatingsCount, + 3, + isoSpeedRatingsOptions); + + // If the EXIF ISO was 65535 and we failed to find anything meaningful in the + // XMP, then we fall back to the EXIF ISO. + + if (checkXMPForHigherISO && (isoSpeedRatingsCount == 0)) + { + + memcpy (exif.fISOSpeedRatings, + oldISOSpeedRatings, + sizeof (oldISOSpeedRatings)); + + } + + // Only remove the ISO tag if there are not ratings over 65535. + + if (removeFromXMP) + { + + bool hasHighISO = false; + + for (uint32 j = 0; j < 3; j++) + { + + if (exif.fISOSpeedRatings [j] == 0) + { + break; + } + + hasHighISO = hasHighISO || (exif.fISOSpeedRatings [j] > 65535); + + } + + if (!hasHighISO) + { + + Remove (XMP_NS_EXIF, "ISOSpeedRatings"); + + } + + } + + } + + // SensitivityType: + + Sync_uint32 (XMP_NS_EXIF, + "SensitivityType", + exif.fSensitivityType, + exif.fSensitivityType == stUnknown, + readOnly + removable); + + // StandardOutputSensitivity: + + Sync_uint32 (XMP_NS_EXIF, + "StandardOutputSensitivity", + exif.fStandardOutputSensitivity, + exif.fStandardOutputSensitivity == 0, + readOnly + removable); + + // RecommendedExposureIndex: + + Sync_uint32 (XMP_NS_EXIF, + "RecommendedExposureIndex", + exif.fRecommendedExposureIndex, + exif.fRecommendedExposureIndex == 0, + readOnly + removable); + + // ISOSpeed: + + Sync_uint32 (XMP_NS_EXIF, + "ISOSpeed", + exif.fISOSpeed, + exif.fISOSpeed == 0, + readOnly + removable); + + // ISOSpeedLatitudeyyy: + + Sync_uint32 (XMP_NS_EXIF, + "ISOSpeedLatitudeyyy", + exif.fISOSpeedLatitudeyyy, + exif.fISOSpeedLatitudeyyy == 0, + readOnly + removable); + + // ISOSpeedLatitudezzz: + + Sync_uint32 (XMP_NS_EXIF, + "ISOSpeedLatitudezzz", + exif.fISOSpeedLatitudezzz, + exif.fISOSpeedLatitudezzz == 0, + readOnly + removable); + + // ExposureIndex: + + Sync_urational (XMP_NS_EXIF, + "ExposureIndex", + exif.fExposureIndex, + readOnly + removable); + + // Brightness Value: + + Sync_srational (XMP_NS_EXIF, + "BrightnessValue", + exif.fBrightnessValue, + readOnly + removable); + + // Exposure Bias: + + Sync_srational (XMP_NS_EXIF, + "ExposureBiasValue", + exif.fExposureBiasValue, + readOnly + removable); + + // Max Aperture: + + Sync_urational (XMP_NS_EXIF, + "MaxApertureValue", + exif.fMaxApertureValue, + readOnly + removable); + + // Subject Distance: + + Sync_urational (XMP_NS_EXIF, + "SubjectDistance", + exif.fSubjectDistance, + readOnly + removable); + + // Metering Mode: + + Sync_uint32 (XMP_NS_EXIF, + "MeteringMode", + exif.fMeteringMode, + exif.fMeteringMode == 0xFFFFFFFF, + readOnly + removable); + + // Light Source: + + Sync_uint32 (XMP_NS_EXIF, + "LightSource", + exif.fLightSource, + exif.fLightSource > 0x0FFFF, + readOnly + removable); + + // Flash State: + + SyncFlash (exif.fFlash, + exif.fFlashMask, + readOnly); + + if (removeFromXMP) + { + Remove (XMP_NS_EXIF, "Flash"); + } + + // Focal Length: + + Sync_urational (XMP_NS_EXIF, + "FocalLength", + exif.fFocalLength, + readOnly + removable); + + // Sensing Method. + + Sync_uint32 (XMP_NS_EXIF, + "SensingMethod", + exif.fSensingMethod, + exif.fSensingMethod > 0x0FFFF, + readOnly + removable); + + // File Source. + + Sync_uint32 (XMP_NS_EXIF, + "FileSource", + exif.fFileSource, + exif.fFileSource > 0x0FF, + readOnly + removable); + + // Scene Type. + + Sync_uint32 (XMP_NS_EXIF, + "SceneType", + exif.fSceneType, + exif.fSceneType > 0x0FF, + readOnly + removable); + + // Focal Length in 35mm Film: + + Sync_uint32 (XMP_NS_EXIF, + "FocalLengthIn35mmFilm", + exif.fFocalLengthIn35mmFilm, + exif.fFocalLengthIn35mmFilm == 0, + readOnly + removable); + + // Custom Rendered: + + Sync_uint32 (XMP_NS_EXIF, + "CustomRendered", + exif.fCustomRendered, + exif.fCustomRendered > 0x0FFFF, + readOnly + removable); + + // Exposure Mode: + + Sync_uint32 (XMP_NS_EXIF, + "ExposureMode", + exif.fExposureMode, + exif.fExposureMode > 0x0FFFF, + readOnly + removable); + + // White Balance: + + Sync_uint32 (XMP_NS_EXIF, + "WhiteBalance", + exif.fWhiteBalance, + exif.fWhiteBalance > 0x0FFFF, + readOnly + removable); + + // Scene Capture Type: + + Sync_uint32 (XMP_NS_EXIF, + "SceneCaptureType", + exif.fSceneCaptureType, + exif.fSceneCaptureType > 0x0FFFF, + readOnly + removable); + + // Gain Control: + + Sync_uint32 (XMP_NS_EXIF, + "GainControl", + exif.fGainControl, + exif.fGainControl > 0x0FFFF, + readOnly + removable); + + // Contrast: + + Sync_uint32 (XMP_NS_EXIF, + "Contrast", + exif.fContrast, + exif.fContrast > 0x0FFFF, + readOnly + removable); + + // Saturation: + + Sync_uint32 (XMP_NS_EXIF, + "Saturation", + exif.fSaturation, + exif.fSaturation > 0x0FFFF, + readOnly + removable); + + // Sharpness: + + Sync_uint32 (XMP_NS_EXIF, + "Sharpness", + exif.fSharpness, + exif.fSharpness > 0x0FFFF, + readOnly + removable); + + // Subject Distance Range: + + Sync_uint32 (XMP_NS_EXIF, + "SubjectDistanceRange", + exif.fSubjectDistanceRange, + exif.fSubjectDistanceRange > 0x0FFFF, + readOnly + removable); + + // Subject Area: + + Sync_uint32_array (XMP_NS_EXIF, + "SubjectArea", + exif.fSubjectArea, + exif.fSubjectAreaCount, + sizeof (exif.fSubjectArea ) / + sizeof (exif.fSubjectArea [0]), + readOnly); + + if (removeFromXMP) + { + Remove (XMP_NS_EXIF, "SubjectArea"); + } + + // Digital Zoom Ratio: + + Sync_urational (XMP_NS_EXIF, + "DigitalZoomRatio", + exif.fDigitalZoomRatio, + readOnly + removable); + + // Focal Plane Resolution: + + Sync_urational (XMP_NS_EXIF, + "FocalPlaneXResolution", + exif.fFocalPlaneXResolution, + readOnly + removable); + + Sync_urational (XMP_NS_EXIF, + "FocalPlaneYResolution", + exif.fFocalPlaneYResolution, + readOnly + removable); + + Sync_uint32 (XMP_NS_EXIF, + "FocalPlaneResolutionUnit", + exif.fFocalPlaneResolutionUnit, + exif.fFocalPlaneResolutionUnit > 0x0FFFF, + readOnly + removable); + + // ImageDescription: (XMP is is always preferred) + + if (fSDK->GetAltLangDefault (XMP_NS_DC, + "description", + exif.fImageDescription)) + + { + + } + + else if (doingUpdateFromXMP) + { + + exif.fImageDescription.Clear (); + + if (originalExif->fImageDescription.NotEmpty ()) + { + + fSDK->SetAltLangDefault (XMP_NS_DC, + "description", + dng_string ()); + + } + + } + + else if (exif.fImageDescription.NotEmpty ()) + { + + fSDK->SetAltLangDefault (XMP_NS_DC, + "description", + exif.fImageDescription); + + } + + // Artist: (XMP is is always preferred) + + { + + dng_string_list xmpList; + + if (fSDK->GetStringList (XMP_NS_DC, + "creator", + xmpList)) + { + + exif.fArtist.Clear (); + + if (xmpList.Count () > 0) + { + + uint32 j; + + uint32 bufferSize = xmpList.Count () * 4 + 1; + + for (j = 0; j < xmpList.Count (); j++) + { + + bufferSize += xmpList [j].Length () * 2; + + } + + dng_memory_data temp (bufferSize); + + char *t = temp.Buffer_char (); + + for (j = 0; j < xmpList.Count (); j++) + { + + const char *s = xmpList [j].Get (); + + bool needQuotes = xmpList [j].Contains ("; ") || + s [0] == '\"'; + + if (needQuotes) + { + *(t++) = '\"'; + } + + while (s [0] != 0) + { + + if (s [0] == '\"' && needQuotes) + { + *(t++) = '\"'; + } + + *(t++) = *(s++); + + } + + if (needQuotes) + { + *(t++) = '\"'; + } + + if (j != xmpList.Count () - 1) + { + *(t++) = ';'; + *(t++) = ' '; + } + else + { + *t = 0; + } + + } + + exif.fArtist.Set (temp.Buffer_char ()); + + } + + } + + else if (doingUpdateFromXMP) + { + + exif.fArtist.Clear (); + + if (originalExif->fArtist.NotEmpty ()) + { + + dng_string_list fakeList; + + fakeList.Append (dng_string ()); + + SetStringList (XMP_NS_DC, + "creator", + fakeList, + false); + + } + + } + + else if (exif.fArtist.NotEmpty ()) + { + + dng_string_list newList; + + dng_memory_data temp (exif.fArtist.Length () + 1); + + const char *s = exif.fArtist.Get (); + + char *t = temp.Buffer_char (); + + bool first = true; + + bool quoted = false; + + bool valid = true; + + while (s [0] != 0 && valid) + { + + if (first) + { + + if (s [0] == '\"') + { + + quoted = true; + + s++; + + } + + } + + first = false; + + if (quoted) + { + + if (s [0] == '\"' && + s [1] == '\"') + { + + s+= 2; + + *(t++) = '\"'; + + } + + else if (s [0] == '\"') + { + + s++; + + quoted = false; + + valid = valid && ((s [0] == 0) || ((s [0] == ';' && s [1] == ' '))); + + } + + else + { + + *(t++) = *(s++); + + } + + } + + else if (s [0] == ';' && + s [1] == ' ') + { + + s += 2; + + t [0] = 0; + + dng_string ss; + + ss.Set (temp.Buffer_char ()); + + newList.Append (ss); + + t = temp.Buffer_char (); + + first = true; + + } + + else + { + + *(t++) = *(s++); + + } + + } + + if (quoted) + { + + valid = false; + + } + + if (valid) + { + + if (t != temp.Buffer_char ()) + { + + t [0] = 0; + + dng_string ss; + + ss.Set (temp.Buffer_char ()); + + newList.Append (ss); + + } + + } + + else + { + + newList.Clear (); + + newList.Append (exif.fArtist); + + } + + SetStringList (XMP_NS_DC, + "creator", + newList, + false); + + } + + } + + // Software: (XMP is is always preferred) + + if (fSDK->GetString (XMP_NS_XAP, + "CreatorTool", + exif.fSoftware)) + + { + + } + + else if (doingUpdateFromXMP) + { + + exif.fSoftware.Clear (); + + if (originalExif->fSoftware.NotEmpty ()) + { + + fSDK->SetString (XMP_NS_XAP, + "CreatorTool", + dng_string ()); + + } + + } + + else if (exif.fSoftware.NotEmpty ()) + { + + fSDK->SetString (XMP_NS_XAP, + "CreatorTool", + exif.fSoftware); + + } + + // Copyright: (XMP is is always preferred) + + if (fSDK->GetAltLangDefault (XMP_NS_DC, + "rights", + exif.fCopyright)) + + { + + } + + else if (doingUpdateFromXMP) + { + + exif.fCopyright.Clear (); + + if (originalExif->fCopyright.NotEmpty ()) + { + + fSDK->SetAltLangDefault (XMP_NS_DC, + "rights", + dng_string ()); + + } + + } + + else if (exif.fCopyright.NotEmpty ()) + { + + fSDK->SetAltLangDefault (XMP_NS_DC, + "rights", + exif.fCopyright); + + } + + // Camera serial number private tag: + + SyncString (XMP_NS_AUX, + "SerialNumber", + exif.fCameraSerialNumber, + readOnly); + + // Lens Info: + + { + + dng_string s; + + if (exif.fLensInfo [0].IsValid ()) + { + + char ss [256]; + + sprintf (ss, + "%u/%u %u/%u %u/%u %u/%u", + (unsigned) exif.fLensInfo [0].n, + (unsigned) exif.fLensInfo [0].d, + (unsigned) exif.fLensInfo [1].n, + (unsigned) exif.fLensInfo [1].d, + (unsigned) exif.fLensInfo [2].n, + (unsigned) exif.fLensInfo [2].d, + (unsigned) exif.fLensInfo [3].n, + (unsigned) exif.fLensInfo [3].d); + + s.Set (ss); + + } + + SyncString (XMP_NS_AUX, + "LensInfo", + s, + readOnly); + + if (s.NotEmpty ()) + { + + unsigned n [4]; + unsigned d [4]; + + if (sscanf (s.Get (), + "%u/%u %u/%u %u/%u %u/%u", + &n [0], + &d [0], + &n [1], + &d [1], + &n [2], + &d [2], + &n [3], + &d [3]) == 8) + { + + for (uint32 j = 0; j < 4; j++) + { + + exif.fLensInfo [j] = dng_urational (n [j], d [j]); + + } + + } + + + } + + } + + // Lens name: + + { + + // EXIF lens names are sometimes missing or wrong (esp. when non-OEM lenses + // are used). So prefer the value from XMP. + + SyncString (XMP_NS_AUX, + "Lens", + exif.fLensName, + preferXMP); + + // Generate default lens name from lens info if required. + // Ignore names names that end in "f/0.0" due to third party bug. + + if ((exif.fLensName.IsEmpty () || + exif.fLensName.EndsWith ("f/0.0")) && exif.fLensInfo [0].IsValid ()) + { + + char s [256]; + + real64 minFL = exif.fLensInfo [0].As_real64 (); + real64 maxFL = exif.fLensInfo [1].As_real64 (); + + // The f-stop numbers are optional. + + if (exif.fLensInfo [2].IsValid ()) + { + + real64 minFS = exif.fLensInfo [2].As_real64 (); + real64 maxFS = exif.fLensInfo [3].As_real64 (); + + if (minFL == maxFL) + sprintf (s, "%.1f mm f/%.1f", minFL, minFS); + + else if (minFS == maxFS) + sprintf (s, "%.1f-%.1f mm f/%.1f", minFL, maxFL, minFS); + + else + sprintf (s, "%.1f-%.1f mm f/%.1f-%.1f", minFL, maxFL, minFS, maxFS); + + } + + else + { + + if (minFL == maxFL) + sprintf (s, "%.1f mm", minFL); + + else + sprintf (s, "%.1f-%.1f mm", minFL, maxFL); + + } + + exif.fLensName.Set (s); + + SetString (XMP_NS_AUX, + "Lens", + exif.fLensName); + + } + + } + + // Lens ID: + + SyncString (XMP_NS_AUX, + "LensID", + exif.fLensID, + readOnly); + + // Lens Make: + + SyncString (XMP_NS_EXIF, + "LensMake", + exif.fLensMake, + readOnly + removable); + + // Lens Serial Number: + + SyncString (XMP_NS_AUX, + "LensSerialNumber", + exif.fLensSerialNumber, + readOnly); + + // Image Number: + + Sync_uint32 (XMP_NS_AUX, + "ImageNumber", + exif.fImageNumber, + exif.fImageNumber == 0xFFFFFFFF, + readOnly); + + // User Comment: + + if (exif.fUserComment.NotEmpty ()) + { + + fSDK->SetAltLangDefault (XMP_NS_EXIF, + "UserComment", + exif.fUserComment); + + } + + else + { + + (void) fSDK->GetAltLangDefault (XMP_NS_EXIF, + "UserComment", + exif.fUserComment); + + } + + if (removeFromXMP) + { + Remove (XMP_NS_EXIF, "UserComment"); + } + + // Approximate focus distance: + + SyncApproximateFocusDistance (exif, + readOnly); + + // Flash Compensation: + + Sync_srational (XMP_NS_AUX, + "FlashCompensation", + exif.fFlashCompensation, + readOnly); + + // Owner Name: (allow XMP updates) + + SyncString (XMP_NS_AUX, + "OwnerName", + exif.fOwnerName, + preferXMP); + + // Firmware: + + SyncString (XMP_NS_AUX, + "Firmware", + exif.fFirmware, + readOnly); + + // Image Unique ID: + + { + + dng_string s = EncodeFingerprint (exif.fImageUniqueID); + + SyncString (XMP_NS_EXIF, + "ImageUniqueID", + s, + readOnly + removable); + + exif.fImageUniqueID = DecodeFingerprint (s); + + } + + // Allow EXIF GPS to be updated via updates from XMP. + + if (doingUpdateFromXMP) + { + + // Require that at least one basic GPS field exist in the + // XMP before overrriding the EXIF GPS fields. + + if (Exists (XMP_NS_EXIF, "GPSVersionID" ) || + Exists (XMP_NS_EXIF, "GPSLatitude" ) || + Exists (XMP_NS_EXIF, "GPSLongitude" ) || + Exists (XMP_NS_EXIF, "GPSAltitude" ) || + Exists (XMP_NS_EXIF, "GPSTimeStamp" ) || + Exists (XMP_NS_EXIF, "GPSProcessingMethod")) + { + + // Clear out the GPS info from the EXIF so it will + // replaced by the GPS info from the XMP. + + dng_exif blankExif; + + exif.CopyGPSFrom (blankExif); + + } + + } + + // GPS Version ID: + + { + + dng_string s = EncodeGPSVersion (exif.fGPSVersionID); + + if (SyncString (XMP_NS_EXIF, + "GPSVersionID", + s, + preferNonXMP + removable)) + { + + exif.fGPSVersionID = DecodeGPSVersion (s); + + } + + } + + // GPS Latitude: + + { + + dng_string s = EncodeGPSCoordinate (exif.fGPSLatitudeRef, + exif.fGPSLatitude); + + if (SyncString (XMP_NS_EXIF, + "GPSLatitude", + s, + preferNonXMP + removable)) + { + + DecodeGPSCoordinate (s, + exif.fGPSLatitudeRef, + exif.fGPSLatitude); + + } + + } + + // GPS Longitude: + + { + + dng_string s = EncodeGPSCoordinate (exif.fGPSLongitudeRef, + exif.fGPSLongitude); + + if (SyncString (XMP_NS_EXIF, + "GPSLongitude", + s, + preferNonXMP + removable)) + { + + DecodeGPSCoordinate (s, + exif.fGPSLongitudeRef, + exif.fGPSLongitude); + + } + + } + + // Handle simple case of incorrectly written GPS altitude where someone didn't understand the GPSAltitudeRef and assumed the GPSAltitude RATIONAL is signed. + // Only handle this case as we do not want to misinterpret e.g. a fixed point representation of very high GPS altitudes. + + uint32 &altitudeRef = exif.fGPSAltitudeRef; + dng_urational &altitude = exif.fGPSAltitude; + + if (altitude.IsValid () && + (altitudeRef == 0 || altitudeRef == 0xFFFFFFFF)) // If the file contains a "below sea level" altitudeRef, assume the writing software is working according to the spec. + { + + if ((altitude.n & (1U << 31)) && + altitude.d < 7) // As the denominator increases, large numerator values become possibly valid distances. Pick a limit on the conservative side (approx 33e6m) to prevent misinterpretation. + // Noting that the normal case for this mistake has a denominator of 1 + { + + altitude.n = ~altitude.n + 1; + altitudeRef = 1; + + } + + } + + // GPS Altitude Reference: + + Sync_uint32 (XMP_NS_EXIF, + "GPSAltitudeRef", + altitudeRef, + altitudeRef == 0xFFFFFFFF, + preferNonXMP + removable); + + // GPS Altitude: + + Sync_urational (XMP_NS_EXIF, + "GPSAltitude", + altitude, + preferNonXMP + removable); + + // GPS Date/Time: + + { + + dng_string s = EncodeGPSDateTime (exif.fGPSDateStamp, + exif.fGPSTimeStamp); + + if (SyncString (XMP_NS_EXIF, + "GPSTimeStamp", + s, + preferNonXMP + removable)) + { + + DecodeGPSDateTime (s, + exif.fGPSDateStamp, + exif.fGPSTimeStamp); + + } + + } + + // GPS Satellites: + + SyncString (XMP_NS_EXIF, + "GPSSatellites", + exif.fGPSSatellites, + preferNonXMP + removable); + + // GPS Status: + + SyncString (XMP_NS_EXIF, + "GPSStatus", + exif.fGPSStatus, + preferNonXMP + removable); + + // GPS Measure Mode: + + SyncString (XMP_NS_EXIF, + "GPSMeasureMode", + exif.fGPSMeasureMode, + preferNonXMP + removable); + + // GPS DOP: + + Sync_urational (XMP_NS_EXIF, + "GPSDOP", + exif.fGPSDOP, + preferNonXMP + removable); + + // GPS Speed Reference: + + SyncString (XMP_NS_EXIF, + "GPSSpeedRef", + exif.fGPSSpeedRef, + preferNonXMP + removable); + + // GPS Speed: + + Sync_urational (XMP_NS_EXIF, + "GPSSpeed", + exif.fGPSSpeed, + preferNonXMP + removable); + + // GPS Track Reference: + + SyncString (XMP_NS_EXIF, + "GPSTrackRef", + exif.fGPSTrackRef, + preferNonXMP + removable); + + // GPS Track: + + Sync_urational (XMP_NS_EXIF, + "GPSTrack", + exif.fGPSTrack, + preferNonXMP + removable); + + // GPS Image Direction Reference: + + SyncString (XMP_NS_EXIF, + "GPSImgDirectionRef", + exif.fGPSImgDirectionRef, + preferNonXMP + removable); + + // GPS Image Direction: + + Sync_urational (XMP_NS_EXIF, + "GPSImgDirection", + exif.fGPSImgDirection, + preferNonXMP + removable); + + // GPS Map Datum: + + SyncString (XMP_NS_EXIF, + "GPSMapDatum", + exif.fGPSMapDatum, + preferNonXMP + removable); + + // GPS Destination Latitude: + + { + + dng_string s = EncodeGPSCoordinate (exif.fGPSDestLatitudeRef, + exif.fGPSDestLatitude); + + if (SyncString (XMP_NS_EXIF, + "GPSDestLatitude", + s, + preferNonXMP + removable)) + { + + DecodeGPSCoordinate (s, + exif.fGPSDestLatitudeRef, + exif.fGPSDestLatitude); + + } + + } + + // GPS Destination Longitude: + + { + + dng_string s = EncodeGPSCoordinate (exif.fGPSDestLongitudeRef, + exif.fGPSDestLongitude); + + if (SyncString (XMP_NS_EXIF, + "GPSDestLongitude", + s, + preferNonXMP + removable)) + { + + DecodeGPSCoordinate (s, + exif.fGPSDestLongitudeRef, + exif.fGPSDestLongitude); + + } + + } + + // GPS Destination Bearing Reference: + + SyncString (XMP_NS_EXIF, + "GPSDestBearingRef", + exif.fGPSDestBearingRef, + preferNonXMP + removable); + + // GPS Destination Bearing: + + Sync_urational (XMP_NS_EXIF, + "GPSDestBearing", + exif.fGPSDestBearing, + preferNonXMP + removable); + + // GPS Destination Distance Reference: + + SyncString (XMP_NS_EXIF, + "GPSDestDistanceRef", + exif.fGPSDestDistanceRef, + preferNonXMP + removable); + + // GPS Destination Distance: + + Sync_urational (XMP_NS_EXIF, + "GPSDestDistance", + exif.fGPSDestDistance, + preferNonXMP + removable); + + // GPS Processing Method: + + SyncString (XMP_NS_EXIF, + "GPSProcessingMethod", + exif.fGPSProcessingMethod, + preferNonXMP + removable); + + // GPS Area Information: + + SyncString (XMP_NS_EXIF, + "GPSAreaInformation", + exif.fGPSAreaInformation, + preferNonXMP + removable); + + // GPS Differential: + + Sync_uint32 (XMP_NS_EXIF, + "GPSDifferential", + exif.fGPSDifferential, + exif.fGPSDifferential == 0xFFFFFFFF, + preferNonXMP + removable); + + // GPS Horizontal Positioning Error: + + Sync_urational (XMP_NS_EXIF, + "GPSHPositioningError", + exif.fGPSHPositioningError, + preferNonXMP + removable); + + // Sync date/times. + + UpdateExifDates (exif, removeFromXMP); + + // We are syncing EXIF and XMP, but we are not updating the + // NativeDigest tags. It is better to just delete them than leave + // the stale values around. + + Remove (XMP_NS_EXIF, "NativeDigest"); + Remove (XMP_NS_TIFF, "NativeDigest"); + + } + +/*****************************************************************************/ + +void dng_xmp::SyncApproximateFocusDistance (dng_exif &exif, + const uint32 readOnly) + { + + Sync_urational (XMP_NS_AUX, + "ApproximateFocusDistance", + exif.fApproxFocusDistance, + readOnly); + + } + +/******************************************************************************/ + +void dng_xmp::ValidateStringList (const char *ns, + const char *path) + { + + fSDK->ValidateStringList (ns, path); + + } + +/******************************************************************************/ + +void dng_xmp::ValidateMetadata () + { + + // The following values should be arrays, but are not always. So + // fix them up because Photoshop sometimes has problems parsing invalid + // tags. + + ValidateStringList (XMP_NS_DC, "creator"); + + ValidateStringList (XMP_NS_PHOTOSHOP, "Keywords"); + ValidateStringList (XMP_NS_PHOTOSHOP, "SupplementalCategories"); + + } + +/******************************************************************************/ + +bool dng_xmp::DateTimeIsDateOnly (const char *ns, + const char *path) + { + + dng_string s; + + if (GetString (ns, path, s)) + { + + uint32 len = s.Length (); + + if (len) + { + + for (uint32 j = 0; j < len; j++) + { + + if (s.Get () [j] == 'T') + { + + return false; + + } + + } + + return true; + + } + + } + + return false; + + } + +/******************************************************************************/ + +void dng_xmp::UpdateExifDates (dng_exif &exif, + bool removeFromXMP) + { + + // For the following three date/time fields, we always prefer XMP to + // the EXIF values. This is to allow the user to correct the date/times + // via changes in a sidecar XMP file, without modifying the original + // raw file. + + // Kludge: The Nikon D4 is writing date only date/times into XMP, so + // prefer the EXIF values if the XMP only contains a date. + + // Modification Date/Time: + // exif.fDateTime + // kXMP_NS_XMP:"ModifyDate" & kXMP_NS_TIFF:"DateTime" are aliased + + { + + dng_string s = exif.fDateTime.Encode_ISO_8601 (); + + bool dateOnly = DateTimeIsDateOnly (XMP_NS_TIFF, "DateTime"); + + SyncString (XMP_NS_TIFF, + "DateTime", + s, + dateOnly ? preferNonXMP : preferXMP); + + if (s.NotEmpty ()) + { + + exif.fDateTime.Decode_ISO_8601 (s.Get ()); + + // Round trip again in case we need to add a fake time zone. + + s = exif.fDateTime.Encode_ISO_8601 (); + + SetString (XMP_NS_TIFF, + "DateTime", + s); + + } + + } + + // Original Date/Time: + // exif.fDateTimeOriginal + // IPTC: DateCreated + // XMP_NS_EXIF:"DateTimeOriginal" & XMP_NS_PHOTOSHOP:"DateCreated" + // Adobe has decided to keep the two XMP fields separate. + + { + + dng_string s = exif.fDateTimeOriginal.Encode_ISO_8601 (); + + bool dateOnly = DateTimeIsDateOnly (XMP_NS_EXIF, "DateTimeOriginal"); + + SyncString (XMP_NS_EXIF, + "DateTimeOriginal", + s, + dateOnly ? preferNonXMP : preferXMP); + + if (s.NotEmpty ()) + { + + exif.fDateTimeOriginal.Decode_ISO_8601 (s.Get ()); + + // Round trip again in case we need to add a fake time zone. + + s = exif.fDateTimeOriginal.Encode_ISO_8601 (); + + SetString (XMP_NS_EXIF, + "DateTimeOriginal", + s); + + } + + // Sync the IPTC value to the EXIF value if only the EXIF + // value exists. + + if (s.NotEmpty () && !Exists (XMP_NS_PHOTOSHOP, "DateCreated")) + { + + SetString (XMP_NS_PHOTOSHOP, "DateCreated", s); + + } + + if (removeFromXMP) + { + + Remove (XMP_NS_EXIF, "DateTimeOriginal"); + + } + + } + + // Date Time Digitized: + // XMP_NS_EXIF:"DateTimeDigitized" & kXMP_NS_XMP:"CreateDate" are aliased + + { + + dng_string s = exif.fDateTimeDigitized.Encode_ISO_8601 (); + + bool dateOnly = DateTimeIsDateOnly (XMP_NS_EXIF, "DateTimeDigitized"); + + SyncString (XMP_NS_EXIF, + "DateTimeDigitized", + s, + dateOnly ? preferNonXMP : preferXMP); + + if (s.NotEmpty ()) + { + + exif.fDateTimeDigitized.Decode_ISO_8601 (s.Get ()); + + // Round trip again in case we need to add a fake time zone. + + s = exif.fDateTimeDigitized.Encode_ISO_8601 (); + + SetString (XMP_NS_EXIF, + "DateTimeDigitized", + s); + + } + + } + + } + +/******************************************************************************/ + +void dng_xmp::UpdateDateTime (const dng_date_time_info &dt) + { + + dng_string s = dt.Encode_ISO_8601 (); + + SetString (XMP_NS_TIFF, + "DateTime", + s); + + } + +/******************************************************************************/ + +void dng_xmp::UpdateMetadataDate (const dng_date_time_info &dt) + { + + dng_string s = dt.Encode_ISO_8601 (); + + SetString (XMP_NS_XAP, + "MetadataDate", + s); + + } + +/*****************************************************************************/ + +bool dng_xmp::HasOrientation () const + { + + uint32 x = 0; + + if (Get_uint32 (XMP_NS_TIFF, + "Orientation", + x)) + { + + return (x >= 1) && (x <= 8); + + } + + return false; + + } + +/*****************************************************************************/ + +dng_orientation dng_xmp::GetOrientation () const + { + + dng_orientation result; + + uint32 x = 0; + + if (Get_uint32 (XMP_NS_TIFF, + "Orientation", + x)) + { + + if ((x >= 1) && (x <= 8)) + { + + result.SetTIFF (x); + + } + + } + + return result; + + } + +/******************************************************************************/ + +void dng_xmp::ClearOrientation () + { + + fSDK->Remove (XMP_NS_TIFF, "Orientation"); + + } + +/******************************************************************************/ + +void dng_xmp::SetOrientation (const dng_orientation &orientation) + { + + Set_uint32 (XMP_NS_TIFF, + "Orientation", + orientation.GetTIFF ()); + + } + +/*****************************************************************************/ + +void dng_xmp::SyncOrientation (dng_negative &negative, + bool xmpIsMaster) + { + + SyncOrientation (negative.Metadata (), xmpIsMaster); + + } + +/*****************************************************************************/ + +void dng_xmp::SyncOrientation (dng_metadata &metadata, + bool xmpIsMaster) + { + + // See if XMP contains the orientation. + + bool xmpHasOrientation = HasOrientation (); + + // See if XMP is the master value. + + if (xmpHasOrientation && (xmpIsMaster || !metadata.HasBaseOrientation ())) + { + + metadata.SetBaseOrientation (GetOrientation ()); + + } + + else + { + + SetOrientation (metadata.BaseOrientation ()); + + } + + } + +/******************************************************************************/ + +void dng_xmp::ClearImageInfo () + { + + Remove (XMP_NS_TIFF, "ImageWidth" ); + Remove (XMP_NS_TIFF, "ImageLength"); + + Remove (XMP_NS_EXIF, "PixelXDimension"); + Remove (XMP_NS_EXIF, "PixelYDimension"); + + Remove (XMP_NS_TIFF, "BitsPerSample"); + + Remove (XMP_NS_TIFF, "Compression"); + + Remove (XMP_NS_TIFF, "PhotometricInterpretation"); + + // "Orientation" is handled separately. + + Remove (XMP_NS_TIFF, "SamplesPerPixel"); + + Remove (XMP_NS_TIFF, "PlanarConfiguration"); + + Remove (XMP_NS_TIFF, "XResolution"); + Remove (XMP_NS_TIFF, "YResolution"); + + Remove (XMP_NS_TIFF, "ResolutionUnit"); + + Remove (XMP_NS_PHOTOSHOP, "ColorMode" ); + Remove (XMP_NS_PHOTOSHOP, "ICCProfile"); + + } + +/******************************************************************************/ + +void dng_xmp::SetImageSize (const dng_point &size) + { + + Set_uint32 (XMP_NS_TIFF, "ImageWidth" , size.h); + Set_uint32 (XMP_NS_TIFF, "ImageLength", size.v); + + // Mirror these values to the EXIF tags. + + Set_uint32 (XMP_NS_EXIF, "PixelXDimension" , size.h); + Set_uint32 (XMP_NS_EXIF, "PixelYDimension" , size.v); + + } + +/******************************************************************************/ + +void dng_xmp::SetSampleInfo (uint32 samplesPerPixel, + uint32 bitsPerSample) + { + + Set_uint32 (XMP_NS_TIFF, "SamplesPerPixel", samplesPerPixel); + + char s [32]; + + sprintf (s, "%u", (unsigned) bitsPerSample); + + dng_string ss; + + ss.Set (s); + + dng_string_list list; + + for (uint32 j = 0; j < samplesPerPixel; j++) + { + list.Append (ss); + } + + SetStringList (XMP_NS_TIFF, "BitsPerSample", list, false); + + } + +/******************************************************************************/ + +void dng_xmp::SetPhotometricInterpretation (uint32 pi) + { + + Set_uint32 (XMP_NS_TIFF, "PhotometricInterpretation", pi); + + } + +/******************************************************************************/ + +void dng_xmp::SetResolution (const dng_resolution &res) + { + + Set_urational (XMP_NS_TIFF, "XResolution", res.fXResolution); + Set_urational (XMP_NS_TIFF, "YResolution", res.fYResolution); + + Set_uint32 (XMP_NS_TIFF, "ResolutionUnit", res.fResolutionUnit); + + } + +/*****************************************************************************/ + +void dng_xmp::ComposeArrayItemPath (const char *ns, + const char *arrayName, + int32 itemNumber, + dng_string &s) const + { + + fSDK->ComposeArrayItemPath (ns, arrayName, itemNumber, s); + + } + +/*****************************************************************************/ + +void dng_xmp::ComposeStructFieldPath (const char *ns, + const char *structName, + const char *fieldNS, + const char *fieldName, + dng_string &s) const + { + + fSDK->ComposeStructFieldPath (ns, structName, fieldNS, fieldName, s); + + } + +/*****************************************************************************/ + +int32 dng_xmp::CountArrayItems (const char *ns, + const char *path) const + { + + return fSDK->CountArrayItems (ns, path); + + } + +/*****************************************************************************/ + +void dng_xmp::AppendArrayItem (const char *ns, + const char *arrayName, + const char *itemValue, + bool isBag, + bool propIsStruct) + { + + fSDK->AppendArrayItem (ns, + arrayName, + itemValue, + isBag, + propIsStruct); + } + +/*****************************************************************************/ + +#if qDNGXMPDocOps + +/*****************************************************************************/ + +void dng_xmp::DocOpsOpenXMP (const char *srcMIMI) + { + + fSDK->DocOpsOpenXMP (srcMIMI); + + } + +/*****************************************************************************/ + +void dng_xmp::DocOpsPrepareForSave (const char *srcMIMI, + const char *dstMIMI, + bool newPath) + { + + fSDK->DocOpsPrepareForSave (srcMIMI, + dstMIMI, + newPath); + + } + +/*****************************************************************************/ + +void dng_xmp::DocOpsUpdateMetadata (const char *srcMIMI) + { + + fSDK->DocOpsUpdateMetadata (srcMIMI); + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_xmp.h b/source/lib/dng_sdk/dng_xmp.h new file mode 100644 index 0000000..8b8319e --- /dev/null +++ b/source/lib/dng_sdk/dng_xmp.h @@ -0,0 +1,405 @@ +/*****************************************************************************/ +// Copyright 2006-2011 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_xmp.h#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_xmp__ +#define __dng_xmp__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" +#include "dng_xmp_sdk.h" + +/*****************************************************************************/ + +class dng_xmp + { + + protected: + + // Sync option bits. + + enum + { + ignoreXMP = 1, // Force XMP values to match non-XMP + preferXMP = 2, // Prefer XMP values if conflict + preferNonXMP = 4, // Prefer non-XMP values if conflict + removeXMP = 8 // Remove XMP value after syncing + }; + + dng_memory_allocator &fAllocator; + + dng_xmp_sdk *fSDK; + + public: + + dng_xmp (dng_memory_allocator &allocator); + + dng_xmp (const dng_xmp &xmp); + + virtual ~dng_xmp (); + + virtual dng_xmp * Clone () const; + + void Parse (dng_host &host, + const void *buffer, + uint32 count); + + dng_memory_block * Serialize (bool asPacket = false, + uint32 targetBytes = 0, + uint32 padBytes = 4096, + bool forJPEG = false, + bool compact = true) const; + + // Kludge: Due to a bug in Premere Elements 9, we need to pass non-compact XMP + // to the host, until we drop support for this Premere version. This bug + // is fixed in Premere Elements 10 and later. + + dng_memory_block * SerializeNonCompact () const + { + return Serialize (false, + 0, + 4096, + false, + false); + } + + void PackageForJPEG (AutoPtr &stdBlock, + AutoPtr &extBlock, + dng_string &extDigest) const; + + void MergeFromJPEG (const dng_xmp &xmp); + + bool HasMeta () const; + + void * GetPrivateMeta (); + + bool Exists (const char *ns, + const char *path) const; + + bool HasNameSpace (const char *ns) const; + + bool IteratePaths (IteratePathsCallback *callback, + void *callbackData, + const char *ns = 0, + const char *path = 0); + + void Remove (const char *ns, + const char *path); + + void RemoveProperties (const char *ns); + + void RemoveEmptyStringOrArray (const char *ns, + const char *path); + + void RemoveEmptyStringsAndArrays (const char *ns = 0); + + void Set (const char *ns, + const char *path, + const char *text); + + bool GetString (const char *ns, + const char *path, + dng_string &s) const; + + void SetString (const char *ns, + const char *path, + const dng_string &s); + + bool GetStringList (const char *ns, + const char *path, + dng_string_list &list) const; + + void SetStringList (const char *ns, + const char *path, + const dng_string_list &list, + bool isBag = false); + + void SetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + const dng_string &s); + + void SetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + const char *s); + + void DeleteStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName); + + bool GetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + dng_string &s) const; + + void SetAltLangDefault (const char *ns, + const char *path, + const dng_string &s); + + bool GetAltLangDefault (const char *ns, + const char *path, + dng_string &s) const; + + bool GetBoolean (const char *ns, + const char *path, + bool &x) const; + + void SetBoolean (const char *ns, + const char *path, + bool x); + + bool Get_int32 (const char *ns, + const char *path, + int32 &x) const; + + void Set_int32 (const char *ns, + const char *path, + int32 x, + bool usePlus = false); + + bool Get_uint32 (const char *ns, + const char *path, + uint32 &x) const; + + void Set_uint32 (const char *ns, + const char *path, + uint32 x); + + bool Get_real64 (const char *ns, + const char *path, + real64 &x) const; + + void Set_real64 (const char *ns, + const char *path, + real64 x, + uint32 places = 6, + bool trim = true, + bool usePlus = false); + + bool Get_urational (const char *ns, + const char *path, + dng_urational &r) const; + + void Set_urational (const char *ns, + const char *path, + const dng_urational &r); + + bool Get_srational (const char *ns, + const char *path, + dng_srational &r) const; + + void Set_srational (const char *ns, + const char *path, + const dng_srational &r); + + bool GetFingerprint (const char *ns, + const char *path, + dng_fingerprint &print) const; + + void SetFingerprint (const char *ns, + const char *path, + const dng_fingerprint &print, + bool allowInvalid = false); + + void SetVersion2to4 (const char *ns, + const char *path, + uint32 version); + + dng_fingerprint GetIPTCDigest () const; + + void SetIPTCDigest (dng_fingerprint &digest); + + void ClearIPTCDigest (); + + void IngestIPTC (dng_metadata &metadata, + bool xmpIsNewer = false); + + void RebuildIPTC (dng_metadata &metadata, + dng_memory_allocator &allocator, + bool padForTIFF); + + virtual void SyncExif (dng_exif &exif, + const dng_exif *originalExif = NULL, + bool doingUpdateFromXMP = false, + bool removeFromXMP = false); + + void ValidateStringList (const char *ns, + const char *path); + + void ValidateMetadata (); + + void UpdateDateTime (const dng_date_time_info &dt); + + void UpdateMetadataDate (const dng_date_time_info &dt); + + void UpdateExifDates (dng_exif &exif, + bool removeFromXMP = false); + + bool HasOrientation () const; + + dng_orientation GetOrientation () const; + + void ClearOrientation (); + + void SetOrientation (const dng_orientation &orientation); + + void SyncOrientation (dng_negative &negative, + bool xmpIsMaster); + // FIX_ME_API: Backwards compatibility + + void SyncOrientation (dng_metadata &metadata, + bool xmpIsMaster); + + void ClearImageInfo (); + + void SetImageSize (const dng_point &size); + + void SetSampleInfo (uint32 samplesPerPixel, + uint32 bitsPerSample); + + void SetPhotometricInterpretation (uint32 pi); + + void SetResolution (const dng_resolution &res); + + void ComposeArrayItemPath (const char *ns, + const char *arrayName, + int32 itemNumber, + dng_string &s) const; + + void ComposeStructFieldPath (const char *ns, + const char *structName, + const char *fieldNS, + const char *fieldName, + dng_string &s) const; + + int32 CountArrayItems (const char *ns, + const char *path) const; + + void AppendArrayItem (const char *ns, + const char *arrayName, + const char *itemValue, + bool isBag = true, + bool propIsStruct = false); + + static dng_string EncodeFingerprint (const dng_fingerprint &f, + bool allowInvalid = false); + + static dng_fingerprint DecodeFingerprint (const dng_string &s); + + #if qDNGXMPDocOps + + void DocOpsOpenXMP (const char *srcMIMI); + + void DocOpsPrepareForSave (const char *srcMIMI, + const char *dstMIMI, + bool newPath = true); + + void DocOpsUpdateMetadata (const char *srcMIMI); + + #endif + + protected: + + static void TrimDecimal (char *s); + + static dng_string EncodeGPSVersion (uint32 version); + + static uint32 DecodeGPSVersion (const dng_string &s); + + static dng_string EncodeGPSCoordinate (const dng_string &ref, + const dng_urational *coord); + + static void DecodeGPSCoordinate (const dng_string &s, + dng_string &ref, + dng_urational *coord); + + static dng_string EncodeGPSDateTime (const dng_string &dateStamp, + const dng_urational *timeStamp); + + static void DecodeGPSDateTime (const dng_string &s, + dng_string &dateStamp, + dng_urational *timeStamp); + + bool SyncString (const char *ns, + const char *path, + dng_string &s, + uint32 options = 0); + + void SyncStringList (const char *ns, + const char *path, + dng_string_list &list, + bool isBag = false, + uint32 options = 0); + + bool SyncAltLangDefault (const char *ns, + const char *path, + dng_string &s, + uint32 options = 0); + + void Sync_uint32 (const char *ns, + const char *path, + uint32 &x, + bool isDefault = false, + uint32 options = 0); + + void Sync_uint32_array (const char *ns, + const char *path, + uint32 *data, + uint32 &count, + uint32 maxCount, + uint32 options = 0); + + void Sync_urational (const char *ns, + const char *path, + dng_urational &r, + uint32 options = 0); + + void Sync_srational (const char *ns, + const char *path, + dng_srational &r, + uint32 options = 0); + + void SyncIPTC (dng_iptc &iptc, + uint32 options); + + void SyncFlash (uint32 &flashState, + uint32 &flashMask, + uint32 options); + + bool DateTimeIsDateOnly (const char *ns, + const char *path); + + virtual void SyncApproximateFocusDistance (dng_exif &exif, + const uint32 readOnly); + + private: + + // Hidden assignment operator. + + dng_xmp & operator= (const dng_xmp &xmp); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_xmp_sdk.cpp b/source/lib/dng_sdk/dng_xmp_sdk.cpp new file mode 100644 index 0000000..fa8ea35 --- /dev/null +++ b/source/lib/dng_sdk/dng_xmp_sdk.cpp @@ -0,0 +1,1670 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_xmp_sdk.cpp#4 $ */ +/* $DateTime: 2012/09/05 12:31:51 $ */ +/* $Change: 847652 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_xmp_sdk.h" + +#include "dng_auto_ptr.h" +#include "dng_assertions.h" +#include "dng_exceptions.h" +#include "dng_flags.h" +#include "dng_host.h" +#include "dng_memory.h" +#include "dng_string.h" +#include "dng_string_list.h" +#include "dng_utils.h" + +#include "stdc_includes.h" + +/*****************************************************************************/ + +#include +#include + +#define TXMP_STRING_TYPE std::string + +#define XMP_INCLUDE_XMPFILES qDNGXMPFiles + +#define XMP_StaticBuild 1 +#undef TXMP_EXPAND_INLINE + +#include "XMP.incl_cpp" + +/*****************************************************************************/ + +const char *XMP_NS_TIFF = "http://ns.adobe.com/tiff/1.0/"; +const char *XMP_NS_EXIF = "http://ns.adobe.com/exif/1.0/"; +const char *XMP_NS_PHOTOSHOP = "http://ns.adobe.com/photoshop/1.0/"; +const char *XMP_NS_XAP = "http://ns.adobe.com/xap/1.0/"; +const char *XMP_NS_XAP_RIGHTS = "http://ns.adobe.com/xap/1.0/rights/"; +const char *XMP_NS_DC = "http://purl.org/dc/elements/1.1/"; +const char *XMP_NS_XMP_NOTE = "http://ns.adobe.com/xmp/note/"; +const char *XMP_NS_MM = "http://ns.adobe.com/xap/1.0/mm/"; + +const char *XMP_NS_CRS = "http://ns.adobe.com/camera-raw-settings/1.0/"; +const char *XMP_NS_CRSS = "http://ns.adobe.com/camera-raw-saved-settings/1.0/"; +const char *XMP_NS_AUX = "http://ns.adobe.com/exif/1.0/aux/"; + +const char *XMP_NS_LCP = "http://ns.adobe.com/photoshop/1.0/camera-profile"; + +const char *XMP_NS_IPTC = "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/"; +const char *XMP_NS_IPTC_EXT = "http://iptc.org/std/Iptc4xmpExt/2008-02-29/"; + +const char *XMP_NS_CRX = "http://ns.adobe.com/lightroom-settings-experimental/1.0/"; + +const char *XMP_NS_DNG = "http://ns.adobe.com/dng/1.0/"; + +/******************************************************************************/ + +#define CATCH_XMP(routine, fatal)\ + \ + catch (std::bad_alloc &)\ + {\ + DNG_REPORT ("Info: XMP " routine " threw memory exception");\ + ThrowMemoryFull ();\ + }\ + \ + catch (XMP_Error &error)\ + {\ + const char *errMessage = error.GetErrMsg ();\ + if (errMessage && strlen (errMessage) <= 128)\ + {\ + char errBuffer [256];\ + sprintf (errBuffer, "Info: XMP " routine " threw '%s' exception", errMessage);\ + DNG_REPORT ( errBuffer);\ + }\ + else\ + {\ + DNG_REPORT ("Info: XMP " routine " threw unnamed exception");\ + }\ + if (fatal) ThrowProgramError ();\ + }\ + \ + catch (...)\ + {\ + DNG_REPORT ("Info: XMP " routine " threw unknown exception");\ + if (fatal) ThrowProgramError ();\ + } + +/*****************************************************************************/ + +class dng_xmp_private + { + + public: + + SXMPMeta *fMeta; + + dng_xmp_private () + : fMeta (NULL) + { + } + + dng_xmp_private (const dng_xmp_private &xmp); + + ~dng_xmp_private () + { + if (fMeta) + { + delete fMeta; + } + } + + private: + + // Hidden assignment operator. + + dng_xmp_private & operator= (const dng_xmp_private &xmp); + + }; + +/*****************************************************************************/ + +dng_xmp_private::dng_xmp_private (const dng_xmp_private &xmp) + + : fMeta (NULL) + + { + + if (xmp.fMeta) + { + + fMeta = new SXMPMeta (xmp.fMeta->Clone (0)); + + if (!fMeta) + { + ThrowMemoryFull (); + } + + } + + } + +/*****************************************************************************/ + +dng_xmp_sdk::dng_xmp_sdk () + + : fPrivate (NULL) + + { + + fPrivate = new dng_xmp_private; + + if (!fPrivate) + { + ThrowMemoryFull (); + } + + } + +/*****************************************************************************/ + +dng_xmp_sdk::dng_xmp_sdk (const dng_xmp_sdk &sdk) + + : fPrivate (NULL) + + { + + fPrivate = new dng_xmp_private (*sdk.fPrivate); + + if (!fPrivate) + { + ThrowMemoryFull (); + } + + } + +/*****************************************************************************/ + +dng_xmp_sdk::~dng_xmp_sdk () + { + + if (fPrivate) + { + delete fPrivate; + } + + } + +/*****************************************************************************/ + +static bool gInitializedXMP = false; + +/*****************************************************************************/ + +void dng_xmp_sdk::InitializeSDK (dng_xmp_namespace * extraNamespaces, + const char *software) + { + + if (!gInitializedXMP) + { + + try + { + + if (!SXMPMeta::Initialize ()) + { + ThrowProgramError (); + } + + // Register Lightroom beta settings namespace. + // We no longer read this but I don't want to cut it out this close + // to a release. [bruzenak] + + { + + TXMP_STRING_TYPE ss; + + SXMPMeta::RegisterNamespace (XMP_NS_CRX, + "crx", + &ss); + + } + + // Register CRSS snapshots namespace + + { + + TXMP_STRING_TYPE ss; + + SXMPMeta::RegisterNamespace (XMP_NS_CRSS, + "crss", + &ss); + + } + + // Register LCP (lens correction profiles) namespace + + { + + TXMP_STRING_TYPE ss; + + SXMPMeta::RegisterNamespace (XMP_NS_LCP, + "stCamera", + &ss); + + } + + // Register DNG format metadata namespace + + { + + TXMP_STRING_TYPE ss; + + SXMPMeta::RegisterNamespace (XMP_NS_DNG, + "dng", + &ss); + + } + + // Register extra namespaces. + + if (extraNamespaces != NULL) + { + + for (; extraNamespaces->fullName != NULL; ++extraNamespaces) + { + + TXMP_STRING_TYPE ss; + + SXMPMeta::RegisterNamespace (extraNamespaces->fullName, + extraNamespaces->shortName, + &ss); + + } + + } + + #if qDNGXMPFiles + + #if qLinux + if (!SXMPFiles::Initialize (kXMPFiles_IgnoreLocalText)) + #else + if (!SXMPFiles::Initialize ()) + #endif + { + ThrowProgramError (); + } + + #endif + + #if qDNGXMPDocOps + + if (software) + { + + SXMPDocOps::SetAppName (software); + + } + + #else + + (void) software; + + #endif + + } + + CATCH_XMP ("Initialization", true) + + gInitializedXMP = true; + + } + + } + +/******************************************************************************/ + +void dng_xmp_sdk::TerminateSDK () + { + + if (gInitializedXMP) + { + + try + { + + #if qDNGXMPFiles + + SXMPFiles::Terminate (); + + #endif + + SXMPMeta::Terminate (); + + } + + catch (...) + { + + } + + gInitializedXMP = false; + + } + + } + +/******************************************************************************/ + +bool dng_xmp_sdk::HasMeta () const + { + + if (fPrivate->fMeta) + { + + return true; + + } + + return false; + + } + +/******************************************************************************/ + +void dng_xmp_sdk::ClearMeta () + { + + if (HasMeta ()) + { + + delete fPrivate->fMeta; + + fPrivate->fMeta = NULL; + + } + + } + +/******************************************************************************/ + +void dng_xmp_sdk::MakeMeta () + { + + ClearMeta (); + + InitializeSDK (); + + try + { + + fPrivate->fMeta = new SXMPMeta; + + if (!fPrivate->fMeta) + { + + ThrowMemoryFull (); + + } + + } + + CATCH_XMP ("MakeMeta", true) + + } + +/******************************************************************************/ + +void dng_xmp_sdk::NeedMeta () + { + + if (!HasMeta ()) + { + + MakeMeta (); + + } + + } + +/******************************************************************************/ + +void * dng_xmp_sdk::GetPrivateMeta () + { + + NeedMeta (); + + return (void *) fPrivate->fMeta; + + } + +/******************************************************************************/ + +void dng_xmp_sdk::Parse (dng_host &host, + const char *buffer, + uint32 count) + { + + MakeMeta (); + + try + { + + try + { + + fPrivate->fMeta->ParseFromBuffer (buffer, count); + + } + + CATCH_XMP ("ParseFromBuffer", true) + + } + + catch (dng_exception &except) + { + + ClearMeta (); + + if (host.IsTransientError (except.ErrorCode ())) + { + + throw; + + } + + ThrowBadFormat (); + + } + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::AppendArrayItem (const char *ns, + const char *arrayName, + const char *itemValue, + bool isBag, + bool propIsStruct) + { + + NeedMeta(); + + try + { + + fPrivate->fMeta->AppendArrayItem (ns, + arrayName, + isBag ? kXMP_PropValueIsArray + : kXMP_PropArrayIsOrdered, + itemValue, + propIsStruct ? kXMP_PropValueIsStruct + : 0); + + } + CATCH_XMP ("AppendArrayItem", true ) + + } + +/*****************************************************************************/ + +int32 dng_xmp_sdk::CountArrayItems (const char *ns, + const char *path) const + { + + if (HasMeta ()) + { + + try + { + + return fPrivate->fMeta->CountArrayItems (ns, path); + + } + + CATCH_XMP ("CountArrayItems", false) + + } + + return 0; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::Exists (const char *ns, + const char *path) const + { + + if (HasMeta ()) + { + + try + { + + return fPrivate->fMeta->DoesPropertyExist (ns, path); + + } + + catch (...) + { + + // Does not exist... + + } + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::HasNameSpace (const char *ns) const + { + + bool result = false; + + if (HasMeta ()) + { + + try + { + + SXMPIterator iter (*fPrivate->fMeta, ns); + + TXMP_STRING_TYPE nsTemp; + TXMP_STRING_TYPE prop; + + if (iter.Next (&nsTemp, + &prop, + NULL, + NULL)) + { + + result = true; + + } + + } + + CATCH_XMP ("HasNameSpace", true) + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::Remove (const char *ns, + const char *path) + { + + if (HasMeta ()) + { + + try + { + + fPrivate->fMeta->DeleteProperty (ns, path); + + } + + CATCH_XMP ("DeleteProperty", false) + + } + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::RemoveProperties (const char *ns) + { + + if (HasMeta ()) + { + + try + { + + SXMPUtils::RemoveProperties (fPrivate->fMeta, + ns, + NULL, + kXMPUtil_DoAllProperties); + + } + + catch (...) + { + + } + + } + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::IsEmptyString (const char *ns, + const char *path) + { + + if (HasMeta ()) + { + + try + { + + TXMP_STRING_TYPE ss; + + XMP_OptionBits options = 0; + + if (fPrivate->fMeta->GetProperty (ns, + path, + &ss, + &options)) + { + + // Item must be simple. + + if (XMP_PropIsSimple (options)) + { + + // Check for null strings. + + return (ss.c_str () == 0 || + ss.c_str () [0] == 0); + + } + + } + + } + + CATCH_XMP ("IsEmptyString", false) + + } + + return false; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::IsEmptyArray (const char *ns, + const char *path) + { + + if (HasMeta ()) + { + + try + { + + TXMP_STRING_TYPE ss; + + XMP_OptionBits options = 0; + + if (fPrivate->fMeta->GetProperty (ns, + path, + &ss, + &options)) + { + + if (XMP_PropIsArray (options)) + { + + if (fPrivate->fMeta->GetArrayItem (ns, + path, + 1, + &ss, + &options)) + { + + // If the first item is a null string... + + if (XMP_PropIsSimple (options)) + { + + if ((ss.c_str () == 0 || + ss.c_str () [0] == 0)) + { + + // And there is no second item. + + if (!fPrivate->fMeta->GetArrayItem (ns, + path, + 2, + &ss, + &options)) + { + + // Then we have an empty array. + + return true; + + } + + } + + } + + } + + else + { + + // Unable to get first item, so array is empty. + + return true; + + } + + } + + } + + } + + CATCH_XMP ("IsEmptyArray", false) + + } + + return false; + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::ComposeArrayItemPath (const char *ns, + const char *arrayName, + int32 index, + dng_string &s) const + { + + try + { + + std::string ss; + + SXMPUtils::ComposeArrayItemPath (ns, arrayName, index, &ss); + + s.Set (ss.c_str ()); + + return; + + } + + CATCH_XMP ("ComposeArrayItemPath", true) + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::ComposeStructFieldPath (const char *ns, + const char *structName, + const char *fieldNS, + const char *fieldName, + dng_string &s) const + { + + try + { + + std::string ss; + + SXMPUtils::ComposeStructFieldPath (ns, + structName, + fieldNS, + fieldName, + &ss); + + s.Set (ss.c_str ()); + + return; + + } + + CATCH_XMP ("ComposeStructFieldPath", true) + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::GetNamespacePrefix (const char *uri, + dng_string &s) const + { + + bool result = false; + + if (HasMeta ()) + { + + try + { + + std::string ss; + + fPrivate->fMeta->GetNamespacePrefix (uri, &ss); + + s.Set (ss.c_str ()); + + result = true; + + } + + CATCH_XMP ("GetNamespacePrefix", false) + + } + + return result; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::GetString (const char *ns, + const char *path, + dng_string &s) const + { + + bool result = false; + + if (HasMeta ()) + { + + try + { + + TXMP_STRING_TYPE ss; + + if (fPrivate->fMeta->GetProperty (ns, path, &ss, NULL)) + { + + s.Set (ss.c_str ()); + + result = true; + + } + + } + + CATCH_XMP ("GetProperty", false) + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::ValidateStringList (const char *ns, + const char *path) + { + + if (Exists (ns, path)) + { + + bool bogus = true; + + try + { + + XMP_Index index = 1; + + TXMP_STRING_TYPE ss; + + while (fPrivate->fMeta->GetArrayItem (ns, + path, + index++, + &ss, + NULL)) + { + + } + + bogus = false; + + } + + CATCH_XMP ("GetArrayItem", false) + + if (bogus) + { + + Remove (ns, path); + + } + + } + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::GetStringList (const char *ns, + const char *path, + dng_string_list &list) const + { + + bool result = false; + + if (HasMeta ()) + { + + try + { + + XMP_Index index = 1; + + TXMP_STRING_TYPE ss; + + while (fPrivate->fMeta->GetArrayItem (ns, + path, + index++, + &ss, + NULL)) + { + + dng_string s; + + s.Set (ss.c_str ()); + + list.Append (s); + + result = true; + + } + + } + + CATCH_XMP ("GetArrayItem", false) + + } + + return result; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::GetAltLangDefault (const char *ns, + const char *path, + dng_string &s) const + { + + bool result = false; + + if (HasMeta ()) + { + + try + { + + TXMP_STRING_TYPE ss; + + if (fPrivate->fMeta->GetLocalizedText (ns, + path, + "x-default", + "x-default", + NULL, + &ss, + NULL)) + { + + s.Set (ss.c_str ()); + + result = true; + + } + // + // Special Case: treat the following two representation equivalently. + // The first is an empty alt lang array; the second is an array with + // an empty item. It seems that xmp lib could be generating both under + // some circumstances! + // + // + // + // + // + // and + // + // + // + // + // + // + // + else if (fPrivate->fMeta->GetProperty (ns, + path, + &ss, + NULL)) + { + + if (ss.empty ()) + { + + s.Clear (); + + result = true; + + } + + } + + } + + CATCH_XMP ("GetLocalizedText", false) + + } + + return result; + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::GetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + dng_string &s) const + { + + bool result = false; + + if (HasMeta ()) + { + + try + { + + TXMP_STRING_TYPE ss; + + if (fPrivate->fMeta->GetStructField (ns, + path, + fieldNS, + fieldName, + &ss, + NULL)) + { + + s.Set (ss.c_str ()); + + result = true; + + } + + } + + CATCH_XMP ("GetStructField", false) + + } + + return result; + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::Set (const char *ns, + const char *path, + const char *text) + { + + NeedMeta (); + + try + { + + fPrivate->fMeta->SetProperty (ns, path, text); + + return; + + } + + catch (...) + { + + // Failed for some reason. + + } + + // Remove existing value and try again. + + Remove (ns, path); + + try + { + + fPrivate->fMeta->SetProperty (ns, path, text); + + } + + CATCH_XMP ("SetProperty", true) + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::SetString (const char *ns, + const char *path, + const dng_string &s) + { + + dng_string ss (s); + + ss.SetLineEndings ('\n'); + + ss.StripLowASCII (); + + Set (ns, path, ss.Get ()); + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::SetStringList (const char *ns, + const char *path, + const dng_string_list &list, + bool isBag) + { + + // Remove any existing structure. + + Remove (ns, path); + + // If list is not empty, add the items. + + if (list.Count ()) + { + + NeedMeta (); + + for (uint32 index = 0; index < list.Count (); index++) + { + + dng_string s (list [index]); + + s.SetLineEndings ('\n'); + + s.StripLowASCII (); + + try + { + + fPrivate->fMeta->AppendArrayItem (ns, + path, + isBag ? kXMP_PropValueIsArray + : kXMP_PropArrayIsOrdered, + s.Get ()); + + } + + CATCH_XMP ("AppendArrayItem", true) + + } + + } + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::SetAltLangDefault (const char *ns, + const char *path, + const dng_string &s) + { + + NeedMeta (); + + Remove (ns, path); + + dng_string ss (s); + + ss.SetLineEndings ('\n'); + + ss.StripLowASCII (); + + try + { + + fPrivate->fMeta->SetLocalizedText (ns, + path, + "x-default", + "x-default", + ss.Get ()); + + } + + CATCH_XMP ("SetLocalizedText", true) + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::SetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + const char *text) + { + + NeedMeta (); + + try + { + + fPrivate->fMeta->SetStructField (ns, + path, + fieldNS, + fieldName, + text); + + } + + CATCH_XMP ("SetStructField", true) + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::DeleteStructField (const char *ns, + const char *structName, + const char *fieldNS, + const char *fieldName) + { + + if (HasMeta ()) + { + + try + { + + fPrivate->fMeta->DeleteStructField (ns, structName, fieldNS, fieldName); + + } + + catch (...) + { + + } + + } + + } + +/*****************************************************************************/ + +dng_memory_block * dng_xmp_sdk::Serialize (dng_memory_allocator &allocator, + bool asPacket, + uint32 targetBytes, + uint32 padBytes, + bool forJPEG, + bool compact) const + { + + // The largest XMP packet you can embed in JPEG using normal methods: + + const uint32 kJPEG_XMP_Limit = 65504; + + if (HasMeta ()) + { + + TXMP_STRING_TYPE s; + + bool havePacket = false; + + // Note that the XMP lib is changing its default to compact format + // in the future, so the following line will need to change. + + uint32 formatOption = compact ? kXMP_UseCompactFormat : 0; + + if (asPacket && targetBytes) + { + + try + { + + fPrivate->fMeta->SerializeToBuffer (&s, + formatOption | kXMP_ExactPacketLength, + targetBytes, + "", + " "); + + havePacket = true; + + } + + catch (...) + { + + // Most likely the packet cannot fit in the target + // byte count. So try again without the limit. + + } + + } + + if (!havePacket) + { + + try + { + + fPrivate->fMeta->SerializeToBuffer (&s, + formatOption | + (asPacket ? 0 + : kXMP_OmitPacketWrapper), + (asPacket ? padBytes + : 0), + "", + " "); + + } + + CATCH_XMP ("SerializeToBuffer", true) + + } + + uint32 packetLen = (uint32) s.size (); + + if (forJPEG && asPacket && padBytes > 0 && targetBytes <= kJPEG_XMP_Limit && + packetLen > kJPEG_XMP_Limit) + { + + uint32 overLimitCount = packetLen - kJPEG_XMP_Limit; + + if (overLimitCount > padBytes) + { + padBytes = 0; + } + else + { + padBytes -= overLimitCount; + } + + try + { + + fPrivate->fMeta->SerializeToBuffer (&s, + formatOption, + padBytes, + "", + " "); + + } + + CATCH_XMP ("SerializeToBuffer", true) + + packetLen = (uint32) s.size (); + + } + + if (packetLen) + { + + AutoPtr buffer (allocator.Allocate (packetLen)); + + memcpy (buffer->Buffer (), s.c_str (), packetLen); + + return buffer.Release (); + + } + + } + + return NULL; + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::PackageForJPEG (dng_memory_allocator &allocator, + AutoPtr &stdBlock, + AutoPtr &extBlock, + dng_string &extDigest) const + { + + if (HasMeta ()) + { + + TXMP_STRING_TYPE stdStr; + TXMP_STRING_TYPE extStr; + TXMP_STRING_TYPE digestStr; + + try + { + + SXMPUtils::PackageForJPEG (*fPrivate->fMeta, + &stdStr, + &extStr, + &digestStr); + + } + + CATCH_XMP ("PackageForJPEG", true) + + uint32 stdLen = (uint32) stdStr.size (); + uint32 extLen = (uint32) extStr.size (); + + if (stdLen) + { + + stdBlock.Reset (allocator.Allocate (stdLen)); + + memcpy (stdBlock->Buffer (), stdStr.c_str (), stdLen); + + } + + if (extLen) + { + + extBlock.Reset (allocator.Allocate (extLen)); + + memcpy (extBlock->Buffer (), extStr.c_str (), extLen); + + if (digestStr.size () != 32) + { + ThrowProgramError (); + } + + extDigest.Set (digestStr.c_str ()); + + } + + } + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::MergeFromJPEG (const dng_xmp_sdk *xmp) + { + + if (xmp && xmp->HasMeta ()) + { + + NeedMeta (); + + try + { + + SXMPUtils::MergeFromJPEG (fPrivate->fMeta, + *xmp->fPrivate->fMeta); + + } + + CATCH_XMP ("MergeFromJPEG", true) + + } + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::ReplaceXMP (dng_xmp_sdk *xmp) + { + + ClearMeta (); + + if (xmp && xmp->HasMeta ()) + { + + fPrivate->fMeta = xmp->fPrivate->fMeta; + + xmp->fPrivate->fMeta = NULL; + + } + + } + +/*****************************************************************************/ + +bool dng_xmp_sdk::IteratePaths (IteratePathsCallback *callback, + void *callbackData, + const char* startingNS, + const char* startingPath) + { + + if (HasMeta ()) + { + + try + { + + SXMPIterator iter (*fPrivate->fMeta, startingNS, startingPath); + + TXMP_STRING_TYPE ns; + TXMP_STRING_TYPE prop; + + while (iter.Next (&ns, + &prop, + NULL, + NULL)) + { + + if (!callback (ns .c_str (), + prop.c_str (), + callbackData)) + { + + return false; + + } + + } + + } + + CATCH_XMP ("IteratePaths", true) + + } + + return true; + + } + +/*****************************************************************************/ + +#if qDNGXMPDocOps + +/*****************************************************************************/ + +void dng_xmp_sdk::DocOpsOpenXMP (const char *srcMIMI) + { + + if (srcMIMI [0]) + { + + NeedMeta (); + + try + { + + SXMPDocOps docOps; + + docOps.OpenXMP (fPrivate->fMeta, + srcMIMI); + + } + + CATCH_XMP ("DocOpsOpenXMP", false) + + Set (XMP_NS_DC, + "format", + srcMIMI); + + } + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::DocOpsPrepareForSave (const char *srcMIMI, + const char *dstMIMI, + bool newPath) + { + + NeedMeta (); + + try + { + + SXMPDocOps docOps; + + docOps.OpenXMP (fPrivate->fMeta, + srcMIMI, + "old path"); + + docOps.NoteChange (kXMP_Part_All); + + docOps.PrepareForSave (dstMIMI, + newPath ? "new path" : "old path"); + + } + + CATCH_XMP ("DocOpsPrepareForSave", false) + + Set (XMP_NS_DC, + "format", + dstMIMI); + + } + +/*****************************************************************************/ + +void dng_xmp_sdk::DocOpsUpdateMetadata (const char *srcMIMI) + { + + NeedMeta (); + + try + { + + SXMPDocOps docOps; + + docOps.OpenXMP (fPrivate->fMeta, + srcMIMI); + + docOps.NoteChange (kXMP_Part_Metadata); + + docOps.PrepareForSave (srcMIMI); + + } + + CATCH_XMP ("DocOpsUpdateMetadata", false) + + } + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_xmp_sdk.h b/source/lib/dng_sdk/dng_xmp_sdk.h new file mode 100644 index 0000000..7f4332b --- /dev/null +++ b/source/lib/dng_sdk/dng_xmp_sdk.h @@ -0,0 +1,238 @@ +/*****************************************************************************/ +// Copyright 2006-2012 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_xmp_sdk.h#2 $ */ +/* $DateTime: 2012/05/31 09:29:29 $ */ +/* $Change: 832505 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#ifndef __dng_xmp_sdk__ +#define __dng_xmp_sdk__ + +/*****************************************************************************/ + +#include "dng_auto_ptr.h" +#include "dng_classes.h" +#include "dng_flags.h" +#include "dng_types.h" + +/*****************************************************************************/ + +extern const char *XMP_NS_TIFF; +extern const char *XMP_NS_EXIF; +extern const char *XMP_NS_PHOTOSHOP; +extern const char *XMP_NS_XAP; +extern const char *XMP_NS_XAP_RIGHTS; +extern const char *XMP_NS_DC; +extern const char *XMP_NS_XMP_NOTE; +extern const char *XMP_NS_MM; + +extern const char *XMP_NS_CRS; +extern const char *XMP_NS_CRSS; + +extern const char *XMP_NS_LCP; + +extern const char *XMP_NS_AUX; + +extern const char *XMP_NS_IPTC; +extern const char *XMP_NS_IPTC_EXT; + +extern const char *XMP_NS_CRX; + +extern const char *XMP_NS_DNG; + +/*****************************************************************************/ + +class dng_xmp_private; + +/*****************************************************************************/ + +typedef bool (IteratePathsCallback) (const char *ns, + const char *path, + void *callbackData); + +/*****************************************************************************/ + +struct dng_xmp_namespace + { + const char * fullName; + const char * shortName; + }; + +/*****************************************************************************/ + +class dng_xmp_sdk + { + + private: + + dng_xmp_private *fPrivate; + + public: + + dng_xmp_sdk (); + + dng_xmp_sdk (const dng_xmp_sdk &sdk); + + virtual ~dng_xmp_sdk (); + + static void InitializeSDK (dng_xmp_namespace * extraNamespaces = NULL, + const char *software = NULL); + + static void TerminateSDK (); + + bool HasMeta () const; + + void * GetPrivateMeta (); + + void Parse (dng_host &host, + const char *buffer, + uint32 count); + + bool Exists (const char *ns, + const char *path) const; + + void AppendArrayItem (const char *ns, + const char *arrayName, + const char *itemValue, + bool isBag = true, + bool propIsStruct = false); + + int32 CountArrayItems (const char *ns, + const char *path) const; + + bool HasNameSpace (const char *ns) const; + + void Remove (const char *ns, + const char *path); + + void RemoveProperties (const char *ns); + + bool IsEmptyString (const char *ns, + const char *path); + + bool IsEmptyArray (const char *ns, + const char *path); + + void ComposeArrayItemPath (const char *ns, + const char *arrayName, + int32 itemNumber, + dng_string &s) const; + + void ComposeStructFieldPath (const char *ns, + const char *structName, + const char *fieldNS, + const char *fieldName, + dng_string &s) const; + + bool GetNamespacePrefix (const char *uri, + dng_string &s) const; + + bool GetString (const char *ns, + const char *path, + dng_string &s) const; + + void ValidateStringList (const char *ns, + const char *path); + + bool GetStringList (const char *ns, + const char *path, + dng_string_list &list) const; + + bool GetAltLangDefault (const char *ns, + const char *path, + dng_string &s) const; + + bool GetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + dng_string &s) const; + + void Set (const char *ns, + const char *path, + const char *text); + + void SetString (const char *ns, + const char *path, + const dng_string &s); + + void SetStringList (const char *ns, + const char *path, + const dng_string_list &list, + bool isBag); + + void SetAltLangDefault (const char *ns, + const char *path, + const dng_string &s); + + void SetStructField (const char *ns, + const char *path, + const char *fieldNS, + const char *fieldName, + const char *text); + + void DeleteStructField (const char *ns, + const char *structName, + const char *fieldNS, + const char *fieldName); + + dng_memory_block * Serialize (dng_memory_allocator &allocator, + bool asPacket, + uint32 targetBytes, + uint32 padBytes, + bool forJPEG, + bool compact) const; + + void PackageForJPEG (dng_memory_allocator &allocator, + AutoPtr &stdBlock, + AutoPtr &extBlock, + dng_string &extDigest) const; + + void MergeFromJPEG (const dng_xmp_sdk *xmp); + + void ReplaceXMP (dng_xmp_sdk *xmp); + + bool IteratePaths (IteratePathsCallback *callback, + void *callbackData = NULL, + const char *startNS = 0, + const char *startingPath = 0); + + #if qDNGXMPDocOps + + void DocOpsOpenXMP (const char *srcMIMI); + + void DocOpsPrepareForSave (const char *srcMIMI, + const char *dstMIMI, + bool newPath = true); + + void DocOpsUpdateMetadata (const char *srcMIMI); + + #endif + + private: + + void ClearMeta (); + + void MakeMeta (); + + void NeedMeta (); + + // Hidden assignment operator. + + dng_xmp_sdk & operator= (const dng_xmp_sdk &sdk); + + }; + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_xy_coord.cpp b/source/lib/dng_sdk/dng_xy_coord.cpp new file mode 100644 index 0000000..c4fe8f0 --- /dev/null +++ b/source/lib/dng_sdk/dng_xy_coord.cpp @@ -0,0 +1,89 @@ +/*****************************************************************************/ +// Copyright 2006-2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_xy_coord.cpp#1 $ */ +/* $DateTime: 2012/05/30 13:28:51 $ */ +/* $Change: 832332 $ */ +/* $Author: tknoll $ */ + +/*****************************************************************************/ + +#include "dng_xy_coord.h" + +#include "dng_matrix.h" +#include "dng_utils.h" + +/******************************************************************************/ + +dng_xy_coord XYZtoXY (const dng_vector_3 &coord) + { + + real64 X = coord [0]; + real64 Y = coord [1]; + real64 Z = coord [2]; + + real64 total = X + Y + Z; + + if (total > 0.0) + { + + return dng_xy_coord (X / total, + Y / total); + + } + + return D50_xy_coord (); + + } + +/*****************************************************************************/ + +dng_vector_3 XYtoXYZ (const dng_xy_coord &coord) + { + + dng_xy_coord temp = coord; + + // Restrict xy coord to someplace inside the range of real xy coordinates. + // This prevents math from doing strange things when users specify + // extreme temperature/tint coordinates. + + temp.x = Pin_real64 (0.000001, temp.x, 0.999999); + temp.y = Pin_real64 (0.000001, temp.y, 0.999999); + + if (temp.x + temp.y > 0.999999) + { + real64 scale = 0.999999 / (temp.x + temp.y); + temp.x *= scale; + temp.y *= scale; + } + + return dng_vector_3 (temp.x / temp.y, + 1.0, + (1.0 - temp.x - temp.y) / temp.y); + + } + +/*****************************************************************************/ + +dng_xy_coord PCStoXY () + { + + return D50_xy_coord (); + + } + +/*****************************************************************************/ + +dng_vector_3 PCStoXYZ () + { + + return XYtoXYZ (PCStoXY ()); + + } + +/*****************************************************************************/ diff --git a/source/lib/dng_sdk/dng_xy_coord.h b/source/lib/dng_sdk/dng_xy_coord.h new file mode 100644 index 0000000..daf2125 --- /dev/null +++ b/source/lib/dng_sdk/dng_xy_coord.h @@ -0,0 +1,187 @@ +/*****************************************************************************/ +// Copyright 2006 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in +// accordance with the terms of the Adobe license agreement accompanying it. +/*****************************************************************************/ + +/* $Id: //mondo/dng_sdk_1_4/dng_sdk/source/dng_xy_coord.h#2 $ */ +/* $DateTime: 2012/07/31 22:04:34 $ */ +/* $Change: 840853 $ */ +/* $Author: tknoll $ */ + +/** \file + * Representation of colors in xy and XYZ coordinates. + */ + +/*****************************************************************************/ + +#ifndef __dng_xy_coord__ +#define __dng_xy_coord__ + +/*****************************************************************************/ + +#include "dng_classes.h" +#include "dng_types.h" + +/*****************************************************************************/ + +class dng_xy_coord + { + + public: + + real64 x; + real64 y; + + public: + + dng_xy_coord () + : x (0.0) + , y (0.0) + { + } + + dng_xy_coord (real64 xx, real64 yy) + : x (xx) + , y (yy) + { + } + + void Clear () + { + x = 0.0; + y = 0.0; + } + + bool IsValid () const + { + return x > 0.0 && + y > 0.0; + } + + bool NotValid () const + { + return !IsValid (); + } + + bool operator== (const dng_xy_coord &coord) const + { + return coord.x == x && + coord.y == y; + } + + bool operator!= (const dng_xy_coord &coord) const + { + return !(*this == coord); + } + + }; + +/*****************************************************************************/ + +inline dng_xy_coord operator+ (const dng_xy_coord &A, + const dng_xy_coord &B) + { + + dng_xy_coord C; + + C.x = A.x + B.x; + C.y = A.y + B.y; + + return C; + + } + +/*****************************************************************************/ + +inline dng_xy_coord operator- (const dng_xy_coord &A, + const dng_xy_coord &B) + { + + dng_xy_coord C; + + C.x = A.x - B.x; + C.y = A.y - B.y; + + return C; + + } + +/*****************************************************************************/ + +inline dng_xy_coord operator* (real64 scale, + const dng_xy_coord &A) + { + + dng_xy_coord B; + + B.x = A.x * scale; + B.y = A.y * scale; + + return B; + + } + +/******************************************************************************/ + +inline real64 operator* (const dng_xy_coord &A, + const dng_xy_coord &B) + { + + return A.x * B.x + + A.y * B.y; + + } + +/*****************************************************************************/ + +// Standard xy coordinate constants. + +inline dng_xy_coord StdA_xy_coord () + { + return dng_xy_coord (0.4476, 0.4074); + } + +inline dng_xy_coord D50_xy_coord () + { + return dng_xy_coord (0.3457, 0.3585); + } + +inline dng_xy_coord D55_xy_coord () + { + return dng_xy_coord (0.3324, 0.3474); + } + +inline dng_xy_coord D65_xy_coord () + { + return dng_xy_coord (0.3127, 0.3290); + } + +inline dng_xy_coord D75_xy_coord () + { + return dng_xy_coord (0.2990, 0.3149); + } + +/*****************************************************************************/ + +// Convert between xy coordinates and XYZ coordinates. + +dng_xy_coord XYZtoXY (const dng_vector_3 &coord); + +dng_vector_3 XYtoXYZ (const dng_xy_coord &coord); + +/*****************************************************************************/ + +// Returns the ICC XYZ profile connection space white point. + +dng_xy_coord PCStoXY (); + +dng_vector_3 PCStoXYZ (); + +/*****************************************************************************/ + +#endif + +/*****************************************************************************/ diff --git a/source/lib/expat_lib/CMakeLists.txt b/source/lib/expat_lib/CMakeLists.txt new file mode 100644 index 0000000..a4c44f8 --- /dev/null +++ b/source/lib/expat_lib/CMakeLists.txt @@ -0,0 +1,21 @@ +# library +set( LIB_NAME expat_lib ) + +# get source files +file( GLOB SRC_FILES "*.c" ) + +# get include files +file( GLOB INC_FILES "*.h" ) + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +# define compile time definitions +target_compile_definitions( ${LIB_NAME} PUBLIC XML_STATIC=1 ) + +SET(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libstdc++") + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/source/lib/expat_lib/amigaconfig.h b/source/lib/expat_lib/amigaconfig.h new file mode 100644 index 0000000..740f565 --- /dev/null +++ b/source/lib/expat_lib/amigaconfig.h @@ -0,0 +1,29 @@ +#ifndef AMIGACONFIG_H +#define AMIGACONFIG_H + +/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ +#define BYTEORDER 4321 + +/* Define to 1 if you have the `bcopy' function. */ +#define HAVE_BCOPY 1 + +/* Define to 1 if you have the header file. */ +#undef HAVE_CHECK_H + +/* Define to 1 if you have the header file. */ +#define HAVE_UNISTD_H 1 + +/* whether byteorder is bigendian */ +#define WORDS_BIGENDIAN + +/* Define to specify how much context to retain around the current parse + point. */ +#define XML_CONTEXT_BYTES 1024 + +/* Define to make parameter entity parsing functionality available. */ +#define XML_DTD + +/* Define to make XML Namespaces functionality available. */ +#define XML_NS + +#endif /* AMIGACONFIG_H */ diff --git a/source/lib/expat_lib/ascii.h b/source/lib/expat_lib/ascii.h new file mode 100644 index 0000000..d10530b --- /dev/null +++ b/source/lib/expat_lib/ascii.h @@ -0,0 +1,92 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#define ASCII_A 0x41 +#define ASCII_B 0x42 +#define ASCII_C 0x43 +#define ASCII_D 0x44 +#define ASCII_E 0x45 +#define ASCII_F 0x46 +#define ASCII_G 0x47 +#define ASCII_H 0x48 +#define ASCII_I 0x49 +#define ASCII_J 0x4A +#define ASCII_K 0x4B +#define ASCII_L 0x4C +#define ASCII_M 0x4D +#define ASCII_N 0x4E +#define ASCII_O 0x4F +#define ASCII_P 0x50 +#define ASCII_Q 0x51 +#define ASCII_R 0x52 +#define ASCII_S 0x53 +#define ASCII_T 0x54 +#define ASCII_U 0x55 +#define ASCII_V 0x56 +#define ASCII_W 0x57 +#define ASCII_X 0x58 +#define ASCII_Y 0x59 +#define ASCII_Z 0x5A + +#define ASCII_a 0x61 +#define ASCII_b 0x62 +#define ASCII_c 0x63 +#define ASCII_d 0x64 +#define ASCII_e 0x65 +#define ASCII_f 0x66 +#define ASCII_g 0x67 +#define ASCII_h 0x68 +#define ASCII_i 0x69 +#define ASCII_j 0x6A +#define ASCII_k 0x6B +#define ASCII_l 0x6C +#define ASCII_m 0x6D +#define ASCII_n 0x6E +#define ASCII_o 0x6F +#define ASCII_p 0x70 +#define ASCII_q 0x71 +#define ASCII_r 0x72 +#define ASCII_s 0x73 +#define ASCII_t 0x74 +#define ASCII_u 0x75 +#define ASCII_v 0x76 +#define ASCII_w 0x77 +#define ASCII_x 0x78 +#define ASCII_y 0x79 +#define ASCII_z 0x7A + +#define ASCII_0 0x30 +#define ASCII_1 0x31 +#define ASCII_2 0x32 +#define ASCII_3 0x33 +#define ASCII_4 0x34 +#define ASCII_5 0x35 +#define ASCII_6 0x36 +#define ASCII_7 0x37 +#define ASCII_8 0x38 +#define ASCII_9 0x39 + +#define ASCII_TAB 0x09 +#define ASCII_SPACE 0x20 +#define ASCII_EXCL 0x21 +#define ASCII_QUOT 0x22 +#define ASCII_AMP 0x26 +#define ASCII_APOS 0x27 +#define ASCII_MINUS 0x2D +#define ASCII_PERIOD 0x2E +#define ASCII_COLON 0x3A +#define ASCII_SEMI 0x3B +#define ASCII_LT 0x3C +#define ASCII_EQUALS 0x3D +#define ASCII_GT 0x3E +#define ASCII_LSQB 0x5B +#define ASCII_RSQB 0x5D +#define ASCII_UNDERSCORE 0x5F +#define ASCII_LPAREN 0x28 +#define ASCII_RPAREN 0x29 +#define ASCII_FF 0x0C +#define ASCII_SLASH 0x2F +#define ASCII_HASH 0x23 +#define ASCII_PIPE 0x7C +#define ASCII_COMMA 0x2C diff --git a/source/lib/expat_lib/asciitab.h b/source/lib/expat_lib/asciitab.h new file mode 100644 index 0000000..79a15c2 --- /dev/null +++ b/source/lib/expat_lib/asciitab.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_CR, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/source/lib/expat_lib/expat.h b/source/lib/expat_lib/expat.h new file mode 100644 index 0000000..9a21680 --- /dev/null +++ b/source/lib/expat_lib/expat.h @@ -0,0 +1,1047 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef Expat_INCLUDED +#define Expat_INCLUDED 1 + +#ifdef __VMS +/* 0 1 2 3 0 1 2 3 + 1234567890123456789012345678901 1234567890123456789012345678901 */ +#define XML_SetProcessingInstructionHandler XML_SetProcessingInstrHandler +#define XML_SetUnparsedEntityDeclHandler XML_SetUnparsedEntDeclHandler +#define XML_SetStartNamespaceDeclHandler XML_SetStartNamespcDeclHandler +#define XML_SetExternalEntityRefHandlerArg XML_SetExternalEntRefHandlerArg +#endif + +#include +#include "expat_external.h" + +#ifdef __cplusplus +extern "C" { +#endif + +struct XML_ParserStruct; +typedef struct XML_ParserStruct *XML_Parser; + +/* Should this be defined using stdbool.h when C99 is available? */ +typedef unsigned char XML_Bool; +#define XML_TRUE ((XML_Bool) 1) +#define XML_FALSE ((XML_Bool) 0) + +/* The XML_Status enum gives the possible return values for several + API functions. The preprocessor #defines are included so this + stanza can be added to code that still needs to support older + versions of Expat 1.95.x: + + #ifndef XML_STATUS_OK + #define XML_STATUS_OK 1 + #define XML_STATUS_ERROR 0 + #endif + + Otherwise, the #define hackery is quite ugly and would have been + dropped. +*/ +enum XML_Status { + XML_STATUS_ERROR = 0, +#define XML_STATUS_ERROR XML_STATUS_ERROR + XML_STATUS_OK = 1, +#define XML_STATUS_OK XML_STATUS_OK + XML_STATUS_SUSPENDED = 2 +#define XML_STATUS_SUSPENDED XML_STATUS_SUSPENDED +}; + +enum XML_Error { + XML_ERROR_NONE, + XML_ERROR_NO_MEMORY, + XML_ERROR_SYNTAX, + XML_ERROR_NO_ELEMENTS, + XML_ERROR_INVALID_TOKEN, + XML_ERROR_UNCLOSED_TOKEN, + XML_ERROR_PARTIAL_CHAR, + XML_ERROR_TAG_MISMATCH, + XML_ERROR_DUPLICATE_ATTRIBUTE, + XML_ERROR_JUNK_AFTER_DOC_ELEMENT, + XML_ERROR_PARAM_ENTITY_REF, + XML_ERROR_UNDEFINED_ENTITY, + XML_ERROR_RECURSIVE_ENTITY_REF, + XML_ERROR_ASYNC_ENTITY, + XML_ERROR_BAD_CHAR_REF, + XML_ERROR_BINARY_ENTITY_REF, + XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF, + XML_ERROR_MISPLACED_XML_PI, + XML_ERROR_UNKNOWN_ENCODING, + XML_ERROR_INCORRECT_ENCODING, + XML_ERROR_UNCLOSED_CDATA_SECTION, + XML_ERROR_EXTERNAL_ENTITY_HANDLING, + XML_ERROR_NOT_STANDALONE, + XML_ERROR_UNEXPECTED_STATE, + XML_ERROR_ENTITY_DECLARED_IN_PE, + XML_ERROR_FEATURE_REQUIRES_XML_DTD, + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING, + /* Added in 1.95.7. */ + XML_ERROR_UNBOUND_PREFIX, + /* Added in 1.95.8. */ + XML_ERROR_UNDECLARING_PREFIX, + XML_ERROR_INCOMPLETE_PE, + XML_ERROR_XML_DECL, + XML_ERROR_TEXT_DECL, + XML_ERROR_PUBLICID, + XML_ERROR_SUSPENDED, + XML_ERROR_NOT_SUSPENDED, + XML_ERROR_ABORTED, + XML_ERROR_FINISHED, + XML_ERROR_SUSPEND_PE, + /* Added in 2.0. */ + XML_ERROR_RESERVED_PREFIX_XML, + XML_ERROR_RESERVED_PREFIX_XMLNS, + XML_ERROR_RESERVED_NAMESPACE_URI +}; + +enum XML_Content_Type { + XML_CTYPE_EMPTY = 1, + XML_CTYPE_ANY, + XML_CTYPE_MIXED, + XML_CTYPE_NAME, + XML_CTYPE_CHOICE, + XML_CTYPE_SEQ +}; + +enum XML_Content_Quant { + XML_CQUANT_NONE, + XML_CQUANT_OPT, + XML_CQUANT_REP, + XML_CQUANT_PLUS +}; + +/* If type == XML_CTYPE_EMPTY or XML_CTYPE_ANY, then quant will be + XML_CQUANT_NONE, and the other fields will be zero or NULL. + If type == XML_CTYPE_MIXED, then quant will be NONE or REP and + numchildren will contain number of elements that may be mixed in + and children point to an array of XML_Content cells that will be + all of XML_CTYPE_NAME type with no quantification. + + If type == XML_CTYPE_NAME, then the name points to the name, and + the numchildren field will be zero and children will be NULL. The + quant fields indicates any quantifiers placed on the name. + + CHOICE and SEQ will have name NULL, the number of children in + numchildren and children will point, recursively, to an array + of XML_Content cells. + + The EMPTY, ANY, and MIXED types will only occur at top level. +*/ + +typedef struct XML_cp XML_Content; + +struct XML_cp { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + XML_Char * name; + unsigned int numchildren; + XML_Content * children; +}; + + +/* This is called for an element declaration. See above for + description of the model argument. It's the caller's responsibility + to free model when finished with it. +*/ +typedef void (XMLCALL *XML_ElementDeclHandler) (void *userData, + const XML_Char *name, + XML_Content *model); + +XMLPARSEAPI(void) +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl); + +/* The Attlist declaration handler is called for *each* attribute. So + a single Attlist declaration with multiple attributes declared will + generate multiple calls to this handler. The "default" parameter + may be NULL in the case of the "#IMPLIED" or "#REQUIRED" + keyword. The "isrequired" parameter will be true and the default + value will be NULL in the case of "#REQUIRED". If "isrequired" is + true and default is non-NULL, then this is a "#FIXED" default. +*/ +typedef void (XMLCALL *XML_AttlistDeclHandler) ( + void *userData, + const XML_Char *elname, + const XML_Char *attname, + const XML_Char *att_type, + const XML_Char *dflt, + int isrequired); + +XMLPARSEAPI(void) +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl); + +/* The XML declaration handler is called for *both* XML declarations + and text declarations. The way to distinguish is that the version + parameter will be NULL for text declarations. The encoding + parameter may be NULL for XML declarations. The standalone + parameter will be -1, 0, or 1 indicating respectively that there + was no standalone parameter in the declaration, that it was given + as no, or that it was given as yes. +*/ +typedef void (XMLCALL *XML_XmlDeclHandler) (void *userData, + const XML_Char *version, + const XML_Char *encoding, + int standalone); + +XMLPARSEAPI(void) +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler xmldecl); + + +typedef struct { + void *(*malloc_fcn)(size_t size); + void *(*realloc_fcn)(void *ptr, size_t size); + void (*free_fcn)(void *ptr); +} XML_Memory_Handling_Suite; + +/* Constructs a new parser; encoding is the encoding specified by the + external protocol or NULL if there is none specified. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreate(const XML_Char *encoding); + +/* Constructs a new parser and namespace processor. Element type + names and attribute names that belong to a namespace will be + expanded; unprefixed attribute names are never expanded; unprefixed + element type names are expanded only if there is a default + namespace. The expanded name is the concatenation of the namespace + URI, the namespace separator character, and the local part of the + name. If the namespace separator is '\0' then the namespace URI + and the local part will be concatenated without any separator. + It is a programming error to use the separator '\0' with namespace + triplets (see XML_SetReturnNSTriplet). +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreateNS(const XML_Char *encoding, XML_Char namespaceSeparator); + + +/* Constructs a new parser using the memory management suite referred to + by memsuite. If memsuite is NULL, then use the standard library memory + suite. If namespaceSeparator is non-NULL it creates a parser with + namespace processing as described above. The character pointed at + will serve as the namespace separator. + + All further memory operations used for the created parser will come from + the given suite. +*/ +XMLPARSEAPI(XML_Parser) +XML_ParserCreate_MM(const XML_Char *encoding, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *namespaceSeparator); + +/* Prepare a parser object to be re-used. This is particularly + valuable when memory allocation overhead is disproportionatly high, + such as when a large number of small documnents need to be parsed. + All handlers are cleared from the parser, except for the + unknownEncodingHandler. The parser's external state is re-initialized + except for the values of ns and ns_triplets. + + Added in Expat 1.95.3. +*/ +XMLPARSEAPI(XML_Bool) +XML_ParserReset(XML_Parser parser, const XML_Char *encoding); + +/* atts is array of name/value pairs, terminated by 0; + names and values are 0 terminated. +*/ +typedef void (XMLCALL *XML_StartElementHandler) (void *userData, + const XML_Char *name, + const XML_Char **atts); + +typedef void (XMLCALL *XML_EndElementHandler) (void *userData, + const XML_Char *name); + + +/* s is not 0 terminated. */ +typedef void (XMLCALL *XML_CharacterDataHandler) (void *userData, + const XML_Char *s, + int len); + +/* target and data are 0 terminated */ +typedef void (XMLCALL *XML_ProcessingInstructionHandler) ( + void *userData, + const XML_Char *target, + const XML_Char *data); + +/* data is 0 terminated */ +typedef void (XMLCALL *XML_CommentHandler) (void *userData, + const XML_Char *data); + +typedef void (XMLCALL *XML_StartCdataSectionHandler) (void *userData); +typedef void (XMLCALL *XML_EndCdataSectionHandler) (void *userData); + +/* This is called for any characters in the XML document for which + there is no applicable handler. This includes both characters that + are part of markup which is of a kind that is not reported + (comments, markup declarations), or characters that are part of a + construct which could be reported but for which no handler has been + supplied. The characters are passed exactly as they were in the XML + document except that they will be encoded in UTF-8 or UTF-16. + Line boundaries are not normalized. Note that a byte order mark + character is not passed to the default handler. There are no + guarantees about how characters are divided between calls to the + default handler: for example, a comment might be split between + multiple calls. +*/ +typedef void (XMLCALL *XML_DefaultHandler) (void *userData, + const XML_Char *s, + int len); + +/* This is called for the start of the DOCTYPE declaration, before + any DTD or internal subset is parsed. +*/ +typedef void (XMLCALL *XML_StartDoctypeDeclHandler) ( + void *userData, + const XML_Char *doctypeName, + const XML_Char *sysid, + const XML_Char *pubid, + int has_internal_subset); + +/* This is called for the start of the DOCTYPE declaration when the + closing > is encountered, but after processing any external + subset. +*/ +typedef void (XMLCALL *XML_EndDoctypeDeclHandler)(void *userData); + +/* This is called for entity declarations. The is_parameter_entity + argument will be non-zero if the entity is a parameter entity, zero + otherwise. + + For internal entities (), value will + be non-NULL and systemId, publicID, and notationName will be NULL. + The value string is NOT nul-terminated; the length is provided in + the value_length argument. Since it is legal to have zero-length + values, do not use this argument to test for internal entities. + + For external entities, value will be NULL and systemId will be + non-NULL. The publicId argument will be NULL unless a public + identifier was provided. The notationName argument will have a + non-NULL value only for unparsed entity declarations. + + Note that is_parameter_entity can't be changed to XML_Bool, since + that would break binary compatibility. +*/ +typedef void (XMLCALL *XML_EntityDeclHandler) ( + void *userData, + const XML_Char *entityName, + int is_parameter_entity, + const XML_Char *value, + int value_length, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +XMLPARSEAPI(void) +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler); + +/* OBSOLETE -- OBSOLETE -- OBSOLETE + This handler has been superceded by the EntityDeclHandler above. + It is provided here for backward compatibility. + + This is called for a declaration of an unparsed (NDATA) entity. + The base argument is whatever was set by XML_SetBase. The + entityName, systemId and notationName arguments will never be + NULL. The other arguments may be. +*/ +typedef void (XMLCALL *XML_UnparsedEntityDeclHandler) ( + void *userData, + const XML_Char *entityName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId, + const XML_Char *notationName); + +/* This is called for a declaration of notation. The base argument is + whatever was set by XML_SetBase. The notationName will never be + NULL. The other arguments can be. +*/ +typedef void (XMLCALL *XML_NotationDeclHandler) ( + void *userData, + const XML_Char *notationName, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* When namespace processing is enabled, these are called once for + each namespace declaration. The call to the start and end element + handlers occur between the calls to the start and end namespace + declaration handlers. For an xmlns attribute, prefix will be + NULL. For an xmlns="" attribute, uri will be NULL. +*/ +typedef void (XMLCALL *XML_StartNamespaceDeclHandler) ( + void *userData, + const XML_Char *prefix, + const XML_Char *uri); + +typedef void (XMLCALL *XML_EndNamespaceDeclHandler) ( + void *userData, + const XML_Char *prefix); + +/* This is called if the document is not standalone, that is, it has an + external subset or a reference to a parameter entity, but does not + have standalone="yes". If this handler returns XML_STATUS_ERROR, + then processing will not continue, and the parser will return a + XML_ERROR_NOT_STANDALONE error. + If parameter entity parsing is enabled, then in addition to the + conditions above this handler will only be called if the referenced + entity was actually read. +*/ +typedef int (XMLCALL *XML_NotStandaloneHandler) (void *userData); + +/* This is called for a reference to an external parsed general + entity. The referenced entity is not automatically parsed. The + application can parse it immediately or later using + XML_ExternalEntityParserCreate. + + The parser argument is the parser parsing the entity containing the + reference; it can be passed as the parser argument to + XML_ExternalEntityParserCreate. The systemId argument is the + system identifier as specified in the entity declaration; it will + not be NULL. + + The base argument is the system identifier that should be used as + the base for resolving systemId if systemId was relative; this is + set by XML_SetBase; it may be NULL. + + The publicId argument is the public identifier as specified in the + entity declaration, or NULL if none was specified; the whitespace + in the public identifier will have been normalized as required by + the XML spec. + + The context argument specifies the parsing context in the format + expected by the context argument to XML_ExternalEntityParserCreate; + context is valid only until the handler returns, so if the + referenced entity is to be parsed later, it must be copied. + context is NULL only when the entity is a parameter entity. + + The handler should return XML_STATUS_ERROR if processing should not + continue because of a fatal error in the handling of the external + entity. In this case the calling parser will return an + XML_ERROR_EXTERNAL_ENTITY_HANDLING error. + + Note that unlike other handlers the first argument is the parser, + not userData. +*/ +typedef int (XMLCALL *XML_ExternalEntityRefHandler) ( + XML_Parser parser, + const XML_Char *context, + const XML_Char *base, + const XML_Char *systemId, + const XML_Char *publicId); + +/* This is called in two situations: + 1) An entity reference is encountered for which no declaration + has been read *and* this is not an error. + 2) An internal entity reference is read, but not expanded, because + XML_SetDefaultHandler has been called. + Note: skipped parameter entities in declarations and skipped general + entities in attribute values cannot be reported, because + the event would be out of sync with the reporting of the + declarations or attribute values +*/ +typedef void (XMLCALL *XML_SkippedEntityHandler) ( + void *userData, + const XML_Char *entityName, + int is_parameter_entity); + +/* This structure is filled in by the XML_UnknownEncodingHandler to + provide information to the parser about encodings that are unknown + to the parser. + + The map[b] member gives information about byte sequences whose + first byte is b. + + If map[b] is c where c is >= 0, then b by itself encodes the + Unicode scalar value c. + + If map[b] is -1, then the byte sequence is malformed. + + If map[b] is -n, where n >= 2, then b is the first byte of an + n-byte sequence that encodes a single Unicode scalar value. + + The data member will be passed as the first argument to the convert + function. + + The convert function is used to convert multibyte sequences; s will + point to a n-byte sequence where map[(unsigned char)*s] == -n. The + convert function must return the Unicode scalar value represented + by this byte sequence or -1 if the byte sequence is malformed. + + The convert function may be NULL if the encoding is a single-byte + encoding, that is if map[b] >= -1 for all bytes b. + + When the parser is finished with the encoding, then if release is + not NULL, it will call release passing it the data member; once + release has been called, the convert function will not be called + again. + + Expat places certain restrictions on the encodings that are supported + using this mechanism. + + 1. Every ASCII character that can appear in a well-formed XML document, + other than the characters + + $@\^`{}~ + + must be represented by a single byte, and that byte must be the + same byte that represents that character in ASCII. + + 2. No character may require more than 4 bytes to encode. + + 3. All characters encoded must have Unicode scalar values <= + 0xFFFF, (i.e., characters that would be encoded by surrogates in + UTF-16 are not allowed). Note that this restriction doesn't + apply to the built-in support for UTF-8 and UTF-16. + + 4. No Unicode character may be encoded by more than one distinct + sequence of bytes. +*/ +typedef struct { + int map[256]; + void *data; + int (XMLCALL *convert)(void *data, const char *s); + void (XMLCALL *release)(void *data); +} XML_Encoding; + +/* This is called for an encoding that is unknown to the parser. + + The encodingHandlerData argument is that which was passed as the + second argument to XML_SetUnknownEncodingHandler. + + The name argument gives the name of the encoding as specified in + the encoding declaration. + + If the callback can provide information about the encoding, it must + fill in the XML_Encoding structure, and return XML_STATUS_OK. + Otherwise it must return XML_STATUS_ERROR. + + If info does not describe a suitable encoding, then the parser will + return an XML_UNKNOWN_ENCODING error. +*/ +typedef int (XMLCALL *XML_UnknownEncodingHandler) ( + void *encodingHandlerData, + const XML_Char *name, + XML_Encoding *info); + +XMLPARSEAPI(void) +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end); + +XMLPARSEAPI(void) +XML_SetStartElementHandler(XML_Parser parser, + XML_StartElementHandler handler); + +XMLPARSEAPI(void) +XML_SetEndElementHandler(XML_Parser parser, + XML_EndElementHandler handler); + +XMLPARSEAPI(void) +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler); + +XMLPARSEAPI(void) +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler); +XMLPARSEAPI(void) +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler); + +XMLPARSEAPI(void) +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end); + +XMLPARSEAPI(void) +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start); + +XMLPARSEAPI(void) +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end); + +/* This sets the default handler and also inhibits expansion of + internal entities. These entity references will be passed to the + default handler, or to the skipped entity handler, if one is set. +*/ +XMLPARSEAPI(void) +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler); + +/* This sets the default handler but does not inhibit expansion of + internal entities. The entity reference will not be passed to the + default handler. +*/ +XMLPARSEAPI(void) +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler); + +XMLPARSEAPI(void) +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end); + +XMLPARSEAPI(void) +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start); + +XMLPARSEAPI(void) +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end); + +XMLPARSEAPI(void) +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler); + +XMLPARSEAPI(void) +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler); + +XMLPARSEAPI(void) +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end); + +XMLPARSEAPI(void) +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start); + +XMLPARSEAPI(void) +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end); + +XMLPARSEAPI(void) +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler); + +XMLPARSEAPI(void) +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler); + +/* If a non-NULL value for arg is specified here, then it will be + passed as the first argument to the external entity ref handler + instead of the parser object. +*/ +XMLPARSEAPI(void) +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, + void *arg); + +XMLPARSEAPI(void) +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler); + +XMLPARSEAPI(void) +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *encodingHandlerData); + +/* This can be called within a handler for a start element, end + element, processing instruction or character data. It causes the + corresponding markup to be passed to the default handler. +*/ +XMLPARSEAPI(void) +XML_DefaultCurrent(XML_Parser parser); + +/* If do_nst is non-zero, and namespace processing is in effect, and + a name has a prefix (i.e. an explicit namespace qualifier) then + that name is returned as a triplet in a single string separated by + the separator character specified when the parser was created: URI + + sep + local_name + sep + prefix. + + If do_nst is zero, then namespace information is returned in the + default manner (URI + sep + local_name) whether or not the name + has a prefix. + + Note: Calling XML_SetReturnNSTriplet after XML_Parse or + XML_ParseBuffer has no effect. +*/ + +XMLPARSEAPI(void) +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst); + +/* This value is passed as the userData argument to callbacks. */ +XMLPARSEAPI(void) +XML_SetUserData(XML_Parser parser, void *userData); + +/* Returns the last value set by XML_SetUserData or NULL. */ +#define XML_GetUserData(parser) (*(void **)(parser)) + +/* This is equivalent to supplying an encoding argument to + XML_ParserCreate. On success XML_SetEncoding returns non-zero, + zero otherwise. + Note: Calling XML_SetEncoding after XML_Parse or XML_ParseBuffer + has no effect and returns XML_STATUS_ERROR. +*/ +XMLPARSEAPI(enum XML_Status) +XML_SetEncoding(XML_Parser parser, const XML_Char *encoding); + +/* If this function is called, then the parser will be passed as the + first argument to callbacks instead of userData. The userData will + still be accessible using XML_GetUserData. +*/ +XMLPARSEAPI(void) +XML_UseParserAsHandlerArg(XML_Parser parser); + +/* If useDTD == XML_TRUE is passed to this function, then the parser + will assume that there is an external subset, even if none is + specified in the document. In such a case the parser will call the + externalEntityRefHandler with a value of NULL for the systemId + argument (the publicId and context arguments will be NULL as well). + Note: For the purpose of checking WFC: Entity Declared, passing + useDTD == XML_TRUE will make the parser behave as if the document + had a DTD with an external subset. + Note: If this function is called, then this must be done before + the first call to XML_Parse or XML_ParseBuffer, since it will + have no effect after that. Returns + XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING. + Note: If the document does not have a DOCTYPE declaration at all, + then startDoctypeDeclHandler and endDoctypeDeclHandler will not + be called, despite an external subset being parsed. + Note: If XML_DTD is not defined when Expat is compiled, returns + XML_ERROR_FEATURE_REQUIRES_XML_DTD. +*/ +XMLPARSEAPI(enum XML_Error) +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD); + + +/* Sets the base to be used for resolving relative URIs in system + identifiers in declarations. Resolving relative identifiers is + left to the application: this value will be passed through as the + base argument to the XML_ExternalEntityRefHandler, + XML_NotationDeclHandler and XML_UnparsedEntityDeclHandler. The base + argument will be copied. Returns XML_STATUS_ERROR if out of memory, + XML_STATUS_OK otherwise. +*/ +XMLPARSEAPI(enum XML_Status) +XML_SetBase(XML_Parser parser, const XML_Char *base); + +XMLPARSEAPI(const XML_Char *) +XML_GetBase(XML_Parser parser); + +/* Returns the number of the attribute/value pairs passed in last call + to the XML_StartElementHandler that were specified in the start-tag + rather than defaulted. Each attribute/value pair counts as 2; thus + this correspondds to an index into the atts array passed to the + XML_StartElementHandler. +*/ +XMLPARSEAPI(int) +XML_GetSpecifiedAttributeCount(XML_Parser parser); + +/* Returns the index of the ID attribute passed in the last call to + XML_StartElementHandler, or -1 if there is no ID attribute. Each + attribute/value pair counts as 2; thus this correspondds to an + index into the atts array passed to the XML_StartElementHandler. +*/ +XMLPARSEAPI(int) +XML_GetIdAttributeIndex(XML_Parser parser); + +#ifdef XML_ATTR_INFO +/* Source file byte offsets for the start and end of attribute names and values. + The value indices are exclusive of surrounding quotes; thus in a UTF-8 source + file an attribute value of "blah" will yield: + info->valueEnd - info->valueStart = 4 bytes. +*/ +typedef struct { + XML_Index nameStart; /* Offset to beginning of the attribute name. */ + XML_Index nameEnd; /* Offset after the attribute name's last byte. */ + XML_Index valueStart; /* Offset to beginning of the attribute value. */ + XML_Index valueEnd; /* Offset after the attribute value's last byte. */ +} XML_AttrInfo; + +/* Returns an array of XML_AttrInfo structures for the attribute/value pairs + passed in last call to the XML_StartElementHandler that were specified + in the start-tag rather than defaulted. Each attribute/value pair counts + as 1; thus the number of entries in the array is + XML_GetSpecifiedAttributeCount(parser) / 2. +*/ +XMLPARSEAPI(const XML_AttrInfo *) +XML_GetAttributeInfo(XML_Parser parser); +#endif + +/* Parses some input. Returns XML_STATUS_ERROR if a fatal error is + detected. The last call to XML_Parse must have isFinal true; len + may be zero for this call (or any other). + + Though the return values for these functions has always been + described as a Boolean value, the implementation, at least for the + 1.95.x series, has always returned exactly one of the XML_Status + values. +*/ +XMLPARSEAPI(enum XML_Status) +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal); + +XMLPARSEAPI(void *) +XML_GetBuffer(XML_Parser parser, int len); + +XMLPARSEAPI(enum XML_Status) +XML_ParseBuffer(XML_Parser parser, int len, int isFinal); + +/* Stops parsing, causing XML_Parse() or XML_ParseBuffer() to return. + Must be called from within a call-back handler, except when aborting + (resumable = 0) an already suspended parser. Some call-backs may + still follow because they would otherwise get lost. Examples: + - endElementHandler() for empty elements when stopped in + startElementHandler(), + - endNameSpaceDeclHandler() when stopped in endElementHandler(), + and possibly others. + + Can be called from most handlers, including DTD related call-backs, + except when parsing an external parameter entity and resumable != 0. + Returns XML_STATUS_OK when successful, XML_STATUS_ERROR otherwise. + Possible error codes: + - XML_ERROR_SUSPENDED: when suspending an already suspended parser. + - XML_ERROR_FINISHED: when the parser has already finished. + - XML_ERROR_SUSPEND_PE: when suspending while parsing an external PE. + + When resumable != 0 (true) then parsing is suspended, that is, + XML_Parse() and XML_ParseBuffer() return XML_STATUS_SUSPENDED. + Otherwise, parsing is aborted, that is, XML_Parse() and XML_ParseBuffer() + return XML_STATUS_ERROR with error code XML_ERROR_ABORTED. + + *Note*: + This will be applied to the current parser instance only, that is, if + there is a parent parser then it will continue parsing when the + externalEntityRefHandler() returns. It is up to the implementation of + the externalEntityRefHandler() to call XML_StopParser() on the parent + parser (recursively), if one wants to stop parsing altogether. + + When suspended, parsing can be resumed by calling XML_ResumeParser(). +*/ +XMLPARSEAPI(enum XML_Status) +XML_StopParser(XML_Parser parser, XML_Bool resumable); + +/* Resumes parsing after it has been suspended with XML_StopParser(). + Must not be called from within a handler call-back. Returns same + status codes as XML_Parse() or XML_ParseBuffer(). + Additional error code XML_ERROR_NOT_SUSPENDED possible. + + *Note*: + This must be called on the most deeply nested child parser instance + first, and on its parent parser only after the child parser has finished, + to be applied recursively until the document entity's parser is restarted. + That is, the parent parser will not resume by itself and it is up to the + application to call XML_ResumeParser() on it at the appropriate moment. +*/ +XMLPARSEAPI(enum XML_Status) +XML_ResumeParser(XML_Parser parser); + +enum XML_Parsing { + XML_INITIALIZED, + XML_PARSING, + XML_FINISHED, + XML_SUSPENDED +}; + +typedef struct { + enum XML_Parsing parsing; + XML_Bool finalBuffer; +} XML_ParsingStatus; + +/* Returns status of parser with respect to being initialized, parsing, + finished, or suspended and processing the final buffer. + XXX XML_Parse() and XML_ParseBuffer() should return XML_ParsingStatus, + XXX with XML_FINISHED_OK or XML_FINISHED_ERROR replacing XML_FINISHED +*/ +XMLPARSEAPI(void) +XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status); + +/* Creates an XML_Parser object that can parse an external general + entity; context is a '\0'-terminated string specifying the parse + context; encoding is a '\0'-terminated string giving the name of + the externally specified encoding, or NULL if there is no + externally specified encoding. The context string consists of a + sequence of tokens separated by formfeeds (\f); a token consisting + of a name specifies that the general entity of the name is open; a + token of the form prefix=uri specifies the namespace for a + particular prefix; a token of the form =uri specifies the default + namespace. This can be called at any point after the first call to + an ExternalEntityRefHandler so longer as the parser has not yet + been freed. The new parser is completely independent and may + safely be used in a separate thread. The handlers and userData are + initialized from the parser argument. Returns NULL if out of memory. + Otherwise returns a new XML_Parser object. +*/ +XMLPARSEAPI(XML_Parser) +XML_ExternalEntityParserCreate(XML_Parser parser, + const XML_Char *context, + const XML_Char *encoding); + +enum XML_ParamEntityParsing { + XML_PARAM_ENTITY_PARSING_NEVER, + XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE, + XML_PARAM_ENTITY_PARSING_ALWAYS +}; + +/* Controls parsing of parameter entities (including the external DTD + subset). If parsing of parameter entities is enabled, then + references to external parameter entities (including the external + DTD subset) will be passed to the handler set with + XML_SetExternalEntityRefHandler. The context passed will be 0. + + Unlike external general entities, external parameter entities can + only be parsed synchronously. If the external parameter entity is + to be parsed, it must be parsed during the call to the external + entity ref handler: the complete sequence of + XML_ExternalEntityParserCreate, XML_Parse/XML_ParseBuffer and + XML_ParserFree calls must be made during this call. After + XML_ExternalEntityParserCreate has been called to create the parser + for the external parameter entity (context must be 0 for this + call), it is illegal to make any calls on the old parser until + XML_ParserFree has been called on the newly created parser. + If the library has been compiled without support for parameter + entity parsing (ie without XML_DTD being defined), then + XML_SetParamEntityParsing will return 0 if parsing of parameter + entities is requested; otherwise it will return non-zero. + Note: If XML_SetParamEntityParsing is called after XML_Parse or + XML_ParseBuffer, then it has no effect and will always return 0. +*/ +XMLPARSEAPI(int) +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing parsing); + +/* Sets the hash salt to use for internal hash calculations. + Helps in preventing DoS attacks based on predicting hash + function behavior. This must be called before parsing is started. + Returns 1 if successful, 0 when called after parsing has started. +*/ +XMLPARSEAPI(int) +XML_SetHashSalt(XML_Parser parser, + unsigned long hash_salt); + +/* If XML_Parse or XML_ParseBuffer have returned XML_STATUS_ERROR, then + XML_GetErrorCode returns information about the error. +*/ +XMLPARSEAPI(enum XML_Error) +XML_GetErrorCode(XML_Parser parser); + +/* These functions return information about the current parse + location. They may be called from any callback called to report + some parse event; in this case the location is the location of the + first of the sequence of characters that generated the event. When + called from callbacks generated by declarations in the document + prologue, the location identified isn't as neatly defined, but will + be within the relevant markup. When called outside of the callback + functions, the position indicated will be just past the last parse + event (regardless of whether there was an associated callback). + + They may also be called after returning from a call to XML_Parse + or XML_ParseBuffer. If the return value is XML_STATUS_ERROR then + the location is the location of the character at which the error + was detected; otherwise the location is the location of the last + parse event, as described above. +*/ +XMLPARSEAPI(XML_Size) XML_GetCurrentLineNumber(XML_Parser parser); +XMLPARSEAPI(XML_Size) XML_GetCurrentColumnNumber(XML_Parser parser); +XMLPARSEAPI(XML_Index) XML_GetCurrentByteIndex(XML_Parser parser); + +/* Return the number of bytes in the current event. + Returns 0 if the event is in an internal entity. +*/ +XMLPARSEAPI(int) +XML_GetCurrentByteCount(XML_Parser parser); + +/* If XML_CONTEXT_BYTES is defined, returns the input buffer, sets + the integer pointed to by offset to the offset within this buffer + of the current parse position, and sets the integer pointed to by size + to the size of this buffer (the number of input bytes). Otherwise + returns a NULL pointer. Also returns a NULL pointer if a parse isn't + active. + + NOTE: The character pointer returned should not be used outside + the handler that makes the call. +*/ +XMLPARSEAPI(const char *) +XML_GetInputContext(XML_Parser parser, + int *offset, + int *size); + +/* For backwards compatibility with previous versions. */ +#define XML_GetErrorLineNumber XML_GetCurrentLineNumber +#define XML_GetErrorColumnNumber XML_GetCurrentColumnNumber +#define XML_GetErrorByteIndex XML_GetCurrentByteIndex + +/* Frees the content model passed to the element declaration handler */ +XMLPARSEAPI(void) +XML_FreeContentModel(XML_Parser parser, XML_Content *model); + +/* Exposing the memory handling functions used in Expat */ +XMLPARSEAPI(void *) +XML_MemMalloc(XML_Parser parser, size_t size); + +XMLPARSEAPI(void *) +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size); + +XMLPARSEAPI(void) +XML_MemFree(XML_Parser parser, void *ptr); + +/* Frees memory used by the parser. */ +XMLPARSEAPI(void) +XML_ParserFree(XML_Parser parser); + +/* Returns a string describing the error. */ +XMLPARSEAPI(const XML_LChar *) +XML_ErrorString(enum XML_Error code); + +/* Return a string containing the version number of this expat */ +XMLPARSEAPI(const XML_LChar *) +XML_ExpatVersion(void); + +typedef struct { + int major; + int minor; + int micro; +} XML_Expat_Version; + +/* Return an XML_Expat_Version structure containing numeric version + number information for this version of expat. +*/ +XMLPARSEAPI(XML_Expat_Version) +XML_ExpatVersionInfo(void); + +/* Added in Expat 1.95.5. */ +enum XML_FeatureEnum { + XML_FEATURE_END = 0, + XML_FEATURE_UNICODE, + XML_FEATURE_UNICODE_WCHAR_T, + XML_FEATURE_DTD, + XML_FEATURE_CONTEXT_BYTES, + XML_FEATURE_MIN_SIZE, + XML_FEATURE_SIZEOF_XML_CHAR, + XML_FEATURE_SIZEOF_XML_LCHAR, + XML_FEATURE_NS, + XML_FEATURE_LARGE_SIZE, + XML_FEATURE_ATTR_INFO + /* Additional features must be added to the end of this enum. */ +}; + +typedef struct { + enum XML_FeatureEnum feature; + const XML_LChar *name; + long int value; +} XML_Feature; + +XMLPARSEAPI(const XML_Feature *) +XML_GetFeatureList(void); + + +/* Expat follows the GNU/Linux convention of odd number minor version for + beta/development releases and even number minor version for stable + releases. Micro is bumped with each release, and set to 0 with each + change to major or minor version. +*/ +#define XML_MAJOR_VERSION 2 +#define XML_MINOR_VERSION 1 +#define XML_MICRO_VERSION 0 + +#ifdef __cplusplus +} +#endif + +#endif /* not Expat_INCLUDED */ diff --git a/source/lib/expat_lib/expat_external.h b/source/lib/expat_lib/expat_external.h new file mode 100644 index 0000000..2c03284 --- /dev/null +++ b/source/lib/expat_lib/expat_external.h @@ -0,0 +1,115 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef Expat_External_INCLUDED +#define Expat_External_INCLUDED 1 + +/* External API definitions */ + +#if defined(_MSC_EXTENSIONS) && !defined(__BEOS__) && !defined(__CYGWIN__) +#define XML_USE_MSC_EXTENSIONS 1 +#endif + +/* Expat tries very hard to make the API boundary very specifically + defined. There are two macros defined to control this boundary; + each of these can be defined before including this header to + achieve some different behavior, but doing so it not recommended or + tested frequently. + + XMLCALL - The calling convention to use for all calls across the + "library boundary." This will default to cdecl, and + try really hard to tell the compiler that's what we + want. + + XMLIMPORT - Whatever magic is needed to note that a function is + to be imported from a dynamically loaded library + (.dll, .so, or .sl, depending on your platform). + + The XMLCALL macro was added in Expat 1.95.7. The only one which is + expected to be directly useful in client code is XMLCALL. + + Note that on at least some Unix versions, the Expat library must be + compiled with the cdecl calling convention as the default since + system headers may assume the cdecl convention. +*/ +#ifndef XMLCALL +#if defined(_MSC_VER) +#define XMLCALL __cdecl +#elif defined(__GNUC__) && defined(__i386) && !defined(__INTEL_COMPILER) +#define XMLCALL __attribute__((cdecl)) +#else +/* For any platform which uses this definition and supports more than + one calling convention, we need to extend this definition to + declare the convention used on that platform, if it's possible to + do so. + + If this is the case for your platform, please file a bug report + with information on how to identify your platform via the C + pre-processor and how to specify the same calling convention as the + platform's malloc() implementation. +*/ +#define XMLCALL +#endif +#endif /* not defined XMLCALL */ + + +#if !defined(XML_STATIC) && !defined(XMLIMPORT) +#ifndef XML_BUILDING_EXPAT +/* using Expat from an application */ + +#ifdef XML_USE_MSC_EXTENSIONS +#define XMLIMPORT __declspec(dllimport) +#endif + +#endif +#endif /* not defined XML_STATIC */ + + +/* If we didn't define it above, define it away: */ +#ifndef XMLIMPORT +#define XMLIMPORT +#endif + + +#define XMLPARSEAPI(type) XMLIMPORT type XMLCALL + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef XML_UNICODE_WCHAR_T +#define XML_UNICODE +#endif + +#ifdef XML_UNICODE /* Information is UTF-16 encoded. */ +#ifdef XML_UNICODE_WCHAR_T +typedef wchar_t XML_Char; +typedef wchar_t XML_LChar; +#else +typedef unsigned short XML_Char; +typedef char XML_LChar; +#endif /* XML_UNICODE_WCHAR_T */ +#else /* Information is UTF-8 encoded. */ +typedef char XML_Char; +typedef char XML_LChar; +#endif /* XML_UNICODE */ + +#ifdef XML_LARGE_SIZE /* Use large integers for file/stream positions. */ +#if defined(XML_USE_MSC_EXTENSIONS) && _MSC_VER < 1400 +typedef __int64 XML_Index; +typedef unsigned __int64 XML_Size; +#else +typedef long long XML_Index; +typedef unsigned long long XML_Size; +#endif +#else +typedef long XML_Index; +typedef unsigned long XML_Size; +#endif /* XML_LARGE_SIZE */ + +#ifdef __cplusplus +} +#endif + +#endif /* not Expat_External_INCLUDED */ diff --git a/source/lib/expat_lib/iasciitab.h b/source/lib/expat_lib/iasciitab.h new file mode 100644 index 0000000..24a1d5c --- /dev/null +++ b/source/lib/expat_lib/iasciitab.h @@ -0,0 +1,37 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* Like asciitab.h, except that 0xD has code BT_S rather than BT_CR */ +/* 0x00 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x04 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x08 */ BT_NONXML, BT_S, BT_LF, BT_NONXML, +/* 0x0C */ BT_NONXML, BT_S, BT_NONXML, BT_NONXML, +/* 0x10 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x14 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x18 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x1C */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0x20 */ BT_S, BT_EXCL, BT_QUOT, BT_NUM, +/* 0x24 */ BT_OTHER, BT_PERCNT, BT_AMP, BT_APOS, +/* 0x28 */ BT_LPAR, BT_RPAR, BT_AST, BT_PLUS, +/* 0x2C */ BT_COMMA, BT_MINUS, BT_NAME, BT_SOL, +/* 0x30 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x34 */ BT_DIGIT, BT_DIGIT, BT_DIGIT, BT_DIGIT, +/* 0x38 */ BT_DIGIT, BT_DIGIT, BT_COLON, BT_SEMI, +/* 0x3C */ BT_LT, BT_EQUALS, BT_GT, BT_QUEST, +/* 0x40 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x44 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x48 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x4C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x50 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x54 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x58 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_LSQB, +/* 0x5C */ BT_OTHER, BT_RSQB, BT_OTHER, BT_NMSTRT, +/* 0x60 */ BT_OTHER, BT_HEX, BT_HEX, BT_HEX, +/* 0x64 */ BT_HEX, BT_HEX, BT_HEX, BT_NMSTRT, +/* 0x68 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x6C */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x70 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x74 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0x78 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0x7C */ BT_VERBAR, BT_OTHER, BT_OTHER, BT_OTHER, diff --git a/source/lib/expat_lib/internal.h b/source/lib/expat_lib/internal.h new file mode 100644 index 0000000..dd54548 --- /dev/null +++ b/source/lib/expat_lib/internal.h @@ -0,0 +1,73 @@ +/* internal.h + + Internal definitions used by Expat. This is not needed to compile + client code. + + The following calling convention macros are defined for frequently + called functions: + + FASTCALL - Used for those internal functions that have a simple + body and a low number of arguments and local variables. + + PTRCALL - Used for functions called though function pointers. + + PTRFASTCALL - Like PTRCALL, but for low number of arguments. + + inline - Used for selected internal functions for which inlining + may improve performance on some platforms. + + Note: Use of these macros is based on judgement, not hard rules, + and therefore subject to change. +*/ + +#if defined(__GNUC__) && defined(__i386__) && !defined(__MINGW32__) +/* We'll use this version by default only where we know it helps. + + regparm() generates warnings on Solaris boxes. See SF bug #692878. + + Instability reported with egcs on a RedHat Linux 7.3. + Let's comment out: + #define FASTCALL __attribute__((stdcall, regparm(3))) + and let's try this: +*/ +#define FASTCALL __attribute__((regparm(3))) +#define PTRFASTCALL __attribute__((regparm(3))) +#endif + +/* Using __fastcall seems to have an unexpected negative effect under + MS VC++, especially for function pointers, so we won't use it for + now on that platform. It may be reconsidered for a future release + if it can be made more effective. + Likely reason: __fastcall on Windows is like stdcall, therefore + the compiler cannot perform stack optimizations for call clusters. +*/ + +/* Make sure all of these are defined if they aren't already. */ + +#ifndef FASTCALL +#define FASTCALL +#endif + +#ifndef PTRCALL +#define PTRCALL +#endif + +#ifndef PTRFASTCALL +#define PTRFASTCALL +#endif + +#ifndef XML_MIN_SIZE +#if !defined(__cplusplus) && !defined(inline) +#ifdef __GNUC__ +#define inline __inline +#endif /* __GNUC__ */ +#endif +#endif /* XML_MIN_SIZE */ + +#ifdef __cplusplus +#define inline inline +#else +#ifndef inline +#define inline +#endif +#endif diff --git a/source/lib/expat_lib/latin1tab.h b/source/lib/expat_lib/latin1tab.h new file mode 100644 index 0000000..53c25d7 --- /dev/null +++ b/source/lib/expat_lib/latin1tab.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* 0x80 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x84 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x88 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x8C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x90 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x94 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x98 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0x9C */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA4 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xA8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xAC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB0 */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xB4 */ BT_OTHER, BT_NMSTRT, BT_OTHER, BT_NAME, +/* 0xB8 */ BT_OTHER, BT_OTHER, BT_NMSTRT, BT_OTHER, +/* 0xBC */ BT_OTHER, BT_OTHER, BT_OTHER, BT_OTHER, +/* 0xC0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xC8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xCC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xD4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xD8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xDC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xE8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xEC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF0 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xF4 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_OTHER, +/* 0xF8 */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, +/* 0xFC */ BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, BT_NMSTRT, diff --git a/source/lib/expat_lib/macconfig.h b/source/lib/expat_lib/macconfig.h new file mode 100644 index 0000000..3579cfc --- /dev/null +++ b/source/lib/expat_lib/macconfig.h @@ -0,0 +1,50 @@ +/*================================================================ +** Copyright 2000, Clark Cooper +** All rights reserved. +** +** This is free software. You are permitted to copy, distribute, or modify +** it under the terms of the MIT/X license (contained in the COPYING file +** with this distribution.) +** +*/ + +#ifndef MACCONFIG_H +#define MACCONFIG_H + + +/* 1234 = LIL_ENDIAN, 4321 = BIGENDIAN */ +#define BYTEORDER 4321 + +/* Define to 1 if you have the `bcopy' function. */ +#undef HAVE_BCOPY + +/* Define to 1 if you have a working `mmap' system call. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the header file. */ +#undef HAVE_UNISTD_H + +/* whether byteorder is bigendian */ +#define WORDS_BIGENDIAN + +/* Define to specify how much context to retain around the current parse + point. */ +#undef XML_CONTEXT_BYTES + +/* Define to make parameter entity parsing functionality available. */ +#define XML_DTD + +/* Define to make XML Namespaces functionality available. */ +#define XML_NS + +/* Define to empty if `const' does not conform to ANSI C. */ +#undef const + +/* Define to `long' if does not define. */ +#define off_t long + +/* Define to `unsigned' if does not define. */ +#undef size_t + + +#endif /* ifndef MACCONFIG_H */ diff --git a/source/lib/expat_lib/nametab.h b/source/lib/expat_lib/nametab.h new file mode 100644 index 0000000..b05e62c --- /dev/null +++ b/source/lib/expat_lib/nametab.h @@ -0,0 +1,150 @@ +static const unsigned namingBitmap[] = { +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x00000000, 0x04000000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00000000, 0xFF7FFFFF, 0xFF7FFFFF, +0xFFFFFFFF, 0x7FF3FFFF, 0xFFFFFDFE, 0x7FFFFFFF, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFE00F, 0xFC31FFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00000003, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFD740, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF0003, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0x0000007F, 0x00000000, 0xFFFF0000, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x000007FE, 0xFFFE0000, +0xFFFFFFFF, 0x7CFFFFFF, 0x002F7FFF, 0x00000060, +0xFFFFFFE0, 0x23FFFFFF, 0xFF000000, 0x00000003, +0xFFF99FE0, 0x03C5FDFF, 0xB0000000, 0x00030003, +0xFFF987E0, 0x036DFDFF, 0x5E000000, 0x001C0000, +0xFFFBAFE0, 0x23EDFDFF, 0x00000000, 0x00000001, +0xFFF99FE0, 0x23CDFDFF, 0xB0000000, 0x00000003, +0xD63DC7E0, 0x03BFC718, 0x00000000, 0x00000000, +0xFFFDDFE0, 0x03EFFDFF, 0x00000000, 0x00000003, +0xFFFDDFE0, 0x03EFFDFF, 0x40000000, 0x00000003, +0xFFFDDFE0, 0x03FFFDFF, 0x00000000, 0x00000003, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x000D7FFF, 0x0000003F, 0x00000000, +0xFEF02596, 0x200D6CAE, 0x0000001F, 0x00000000, +0x00000000, 0x00000000, 0xFFFFFEFF, 0x000003FF, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0xFFFFFFFF, 0xFFFF003F, 0x007FFFFF, +0x0007DAED, 0x50000000, 0x82315001, 0x002C62AB, +0x40000000, 0xF580C900, 0x00000007, 0x02010800, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0x0FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x03FFFFFF, +0x3F3FFFFF, 0xFFFFFFFF, 0xAAFF3F3F, 0x3FFFFFFF, +0xFFFFFFFF, 0x5FDFFFFF, 0x0FCF1FDC, 0x1FDC1FFF, +0x00000000, 0x00004C40, 0x00000000, 0x00000000, +0x00000007, 0x00000000, 0x00000000, 0x00000000, +0x00000080, 0x000003FE, 0xFFFFFFFE, 0xFFFFFFFF, +0x001FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x07FFFFFF, +0xFFFFFFE0, 0x00001FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000003F, 0x00000000, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, +0xFFFFFFFF, 0x0000000F, 0x00000000, 0x00000000, +0x00000000, 0x07FF6000, 0x87FFFFFE, 0x07FFFFFE, +0x00000000, 0x00800000, 0xFF7FFFFF, 0xFF7FFFFF, +0x00FFFFFF, 0x00000000, 0xFFFF0000, 0xFFFFFFFF, +0xFFFFFFFF, 0xF80001FF, 0x00030003, 0x00000000, +0xFFFFFFFF, 0xFFFFFFFF, 0x0000003F, 0x00000003, +0xFFFFD7C0, 0xFFFFFFFB, 0x547F7FFF, 0x000FFFFD, +0xFFFFDFFE, 0xFFFFFFFF, 0xDFFEFFFF, 0xFFFFFFFF, +0xFFFF007B, 0xFFFFFFFF, 0xFFFF199F, 0x033FCFFF, +0x00000000, 0xFFFE0000, 0x027FFFFF, 0xFFFFFFFE, +0xFFFE007F, 0xBBFFFFFB, 0xFFFF0016, 0x000707FF, +0x00000000, 0x07FFFFFE, 0x0007FFFF, 0xFFFF03FF, +0xFFFFFFFF, 0x7CFFFFFF, 0xFFEF7FFF, 0x03FF3DFF, +0xFFFFFFEE, 0xF3FFFFFF, 0xFF1E3FFF, 0x0000FFCF, +0xFFF99FEE, 0xD3C5FDFF, 0xB080399F, 0x0003FFCF, +0xFFF987E4, 0xD36DFDFF, 0x5E003987, 0x001FFFC0, +0xFFFBAFEE, 0xF3EDFDFF, 0x00003BBF, 0x0000FFC1, +0xFFF99FEE, 0xF3CDFDFF, 0xB0C0398F, 0x0000FFC3, +0xD63DC7EC, 0xC3BFC718, 0x00803DC7, 0x0000FF80, +0xFFFDDFEE, 0xC3EFFDFF, 0x00603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3EFFDFF, 0x40603DDF, 0x0000FFC3, +0xFFFDDFEC, 0xC3FFFDFF, 0x00803DCF, 0x0000FFC3, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0xFFFFFFFE, 0x07FF7FFF, 0x03FF7FFF, 0x00000000, +0xFEF02596, 0x3BFF6CAE, 0x03FF3F5F, 0x00000000, +0x03000000, 0xC2A003FF, 0xFFFFFEFF, 0xFFFE03FF, +0xFEBF0FDF, 0x02FE3FFF, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x00000000, 0x00000000, +0x00000000, 0x00000000, 0x1FFF0000, 0x00000002, +0x000000A0, 0x003EFFFE, 0xFFFFFFFE, 0xFFFFFFFF, +0x661FFFFF, 0xFFFFFFFE, 0xFFFFFFFF, 0x77FFFFFF, +}; +static const unsigned char nmstrtPages[] = { +0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x00, +0x00, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x15, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; +static const unsigned char namePages[] = { +0x19, 0x03, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x00, +0x00, 0x1F, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, +0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12, 0x13, +0x26, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x27, 0x16, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x17, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, +0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x18, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; diff --git a/source/lib/expat_lib/utf8tab.h b/source/lib/expat_lib/utf8tab.h new file mode 100644 index 0000000..7bb3e77 --- /dev/null +++ b/source/lib/expat_lib/utf8tab.h @@ -0,0 +1,37 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + + +/* 0x80 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x84 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x88 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x8C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x90 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x94 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x98 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0x9C */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xA8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xAC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB0 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB4 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xB8 */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xBC */ BT_TRAIL, BT_TRAIL, BT_TRAIL, BT_TRAIL, +/* 0xC0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xC8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xCC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD0 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD4 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xD8 */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xDC */ BT_LEAD2, BT_LEAD2, BT_LEAD2, BT_LEAD2, +/* 0xE0 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE4 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xE8 */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xEC */ BT_LEAD3, BT_LEAD3, BT_LEAD3, BT_LEAD3, +/* 0xF0 */ BT_LEAD4, BT_LEAD4, BT_LEAD4, BT_LEAD4, +/* 0xF4 */ BT_LEAD4, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xF8 */ BT_NONXML, BT_NONXML, BT_NONXML, BT_NONXML, +/* 0xFC */ BT_NONXML, BT_NONXML, BT_MALFORM, BT_MALFORM, diff --git a/source/lib/expat_lib/winconfig.h b/source/lib/expat_lib/winconfig.h new file mode 100644 index 0000000..c4e2646 --- /dev/null +++ b/source/lib/expat_lib/winconfig.h @@ -0,0 +1,27 @@ +/*================================================================ +** Copyright 2000, Clark Cooper +** All rights reserved. +** +** This is free software. You are permitted to copy, distribute, or modify +** it under the terms of the MIT/X license (contained in the COPYING file +** with this distribution.) +*/ + +#ifndef WINCONFIG_H +#define WINCONFIG_H + +#define WIN32_LEAN_AND_MEAN +#include +#undef WIN32_LEAN_AND_MEAN + +#include +#include + +#define XML_NS 1 +#define XML_DTD 1 +#define XML_CONTEXT_BYTES 1024 + +/* we will assume all Windows platforms are little endian */ +#define BYTEORDER 1234 + +#endif /* ndef WINCONFIG_H */ diff --git a/source/lib/expat_lib/xmlparse.c b/source/lib/expat_lib/xmlparse.c new file mode 100644 index 0000000..760d17d --- /dev/null +++ b/source/lib/expat_lib/xmlparse.c @@ -0,0 +1,6394 @@ +/* Copyright (c) 1998, 1999, 2000 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include +#include /* memset(), memcpy() */ +#include +#include /* UINT_MAX */ +#include /* time() */ + +#define XML_BUILDING_EXPAT 1 + +#ifdef COMPILED_FROM_DSP +#include "winconfig.h" +#elif defined(MACOS_CLASSIC) +#include "macconfig.h" +#elif defined(__amigaos__) +#include "amigaconfig.h" +#elif defined(__WATCOMC__) +#include "watcomconfig.h" +#elif defined(HAVE_EXPAT_CONFIG_H) +#include +#endif /* ndef COMPILED_FROM_DSP */ + +#include "ascii.h" +#include "expat.h" + +#ifdef XML_UNICODE +#define XML_ENCODE_MAX XML_UTF16_ENCODE_MAX +#define XmlConvert XmlUtf16Convert +#define XmlGetInternalEncoding XmlGetUtf16InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf16InternalEncodingNS +#define XmlEncode XmlUtf16Encode +/* Using pointer subtraction to convert to integer type. */ +#define MUST_CONVERT(enc, s) (!(enc)->isUtf16 || (((char *)(s) - (char *)NULL) & 1)) +typedef unsigned short ICHAR; +#else +#define XML_ENCODE_MAX XML_UTF8_ENCODE_MAX +#define XmlConvert XmlUtf8Convert +#define XmlGetInternalEncoding XmlGetUtf8InternalEncoding +#define XmlGetInternalEncodingNS XmlGetUtf8InternalEncodingNS +#define XmlEncode XmlUtf8Encode +#define MUST_CONVERT(enc, s) (!(enc)->isUtf8) +typedef char ICHAR; +#endif + + +#ifndef XML_NS + +#define XmlInitEncodingNS XmlInitEncoding +#define XmlInitUnknownEncodingNS XmlInitUnknownEncoding +#undef XmlGetInternalEncodingNS +#define XmlGetInternalEncodingNS XmlGetInternalEncoding +#define XmlParseXmlDeclNS XmlParseXmlDecl + +#endif + +#ifdef XML_UNICODE + +#ifdef XML_UNICODE_WCHAR_T +#define XML_T(x) (const wchar_t)x +#define XML_L(x) L ## x +#else +#define XML_T(x) (const unsigned short)x +#define XML_L(x) x +#endif + +#else + +#define XML_T(x) x +#define XML_L(x) x + +#endif + +/* Round up n to be a multiple of sz, where sz is a power of 2. */ +#define ROUND_UP(n, sz) (((n) + ((sz) - 1)) & ~((sz) - 1)) + +#include "internal.h" +#include "xmltok.h" +#include "xmlrole.h" + +typedef const XML_Char *KEY; + +typedef struct { + KEY name; +} NAMED; + +typedef struct { + NAMED **v; + unsigned char power; + size_t size; + size_t used; + const XML_Memory_Handling_Suite *mem; +} HASH_TABLE; + +/* Basic character hash algorithm, taken from Python's string hash: + h = h * 1000003 ^ character, the constant being a prime number. + +*/ +#ifdef XML_UNICODE +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned short)(c)) +#else +#define CHAR_HASH(h, c) \ + (((h) * 0xF4243) ^ (unsigned char)(c)) +#endif + +/* For probing (after a collision) we need a step size relative prime + to the hash table size, which is a power of 2. We use double-hashing, + since we can calculate a second hash value cheaply by taking those bits + of the first hash value that were discarded (masked out) when the table + index was calculated: index = hash & mask, where mask = table->size - 1. + We limit the maximum step size to table->size / 4 (mask >> 2) and make + it odd, since odd numbers are always relative prime to a power of 2. +*/ +#define SECOND_HASH(hash, mask, power) \ + ((((hash) & ~(mask)) >> ((power) - 1)) & ((mask) >> 2)) +#define PROBE_STEP(hash, mask, power) \ + ((unsigned char)((SECOND_HASH(hash, mask, power)) | 1)) + +typedef struct { + NAMED **p; + NAMED **end; +} HASH_TABLE_ITER; + +#define INIT_TAG_BUF_SIZE 32 /* must be a multiple of sizeof(XML_Char) */ +#define INIT_DATA_BUF_SIZE 1024 +#define INIT_ATTS_SIZE 16 +#define INIT_ATTS_VERSION 0xFFFFFFFF +#define INIT_BLOCK_SIZE 1024 +#define INIT_BUFFER_SIZE 1024 + +#define EXPAND_SPARE 24 + +typedef struct binding { + struct prefix *prefix; + struct binding *nextTagBinding; + struct binding *prevPrefixBinding; + const struct attribute_id *attId; + XML_Char *uri; + int uriLen; + int uriAlloc; +} BINDING; + +typedef struct prefix { + const XML_Char *name; + BINDING *binding; +} PREFIX; + +typedef struct { + const XML_Char *str; + const XML_Char *localPart; + const XML_Char *prefix; + int strLen; + int uriLen; + int prefixLen; +} TAG_NAME; + +/* TAG represents an open element. + The name of the element is stored in both the document and API + encodings. The memory buffer 'buf' is a separately-allocated + memory area which stores the name. During the XML_Parse()/ + XMLParseBuffer() when the element is open, the memory for the 'raw' + version of the name (in the document encoding) is shared with the + document buffer. If the element is open across calls to + XML_Parse()/XML_ParseBuffer(), the buffer is re-allocated to + contain the 'raw' name as well. + + A parser re-uses these structures, maintaining a list of allocated + TAG objects in a free list. +*/ +typedef struct tag { + struct tag *parent; /* parent of this element */ + const char *rawName; /* tagName in the original encoding */ + int rawNameLength; + TAG_NAME name; /* tagName in the API encoding */ + char *buf; /* buffer for name components */ + char *bufEnd; /* end of the buffer */ + BINDING *bindings; +} TAG; + +typedef struct { + const XML_Char *name; + const XML_Char *textPtr; + int textLen; /* length in XML_Chars */ + int processed; /* # of processed bytes - when suspended */ + const XML_Char *systemId; + const XML_Char *base; + const XML_Char *publicId; + const XML_Char *notation; + XML_Bool open; + XML_Bool is_param; + XML_Bool is_internal; /* true if declared in internal subset outside PE */ +} ENTITY; + +typedef struct { + enum XML_Content_Type type; + enum XML_Content_Quant quant; + const XML_Char * name; + int firstchild; + int lastchild; + int childcnt; + int nextsib; +} CONTENT_SCAFFOLD; + +#define INIT_SCAFFOLD_ELEMENTS 32 + +typedef struct block { + struct block *next; + int size; + XML_Char s[1]; +} BLOCK; + +typedef struct { + BLOCK *blocks; + BLOCK *freeBlocks; + const XML_Char *end; + XML_Char *ptr; + XML_Char *start; + const XML_Memory_Handling_Suite *mem; +} STRING_POOL; + +/* The XML_Char before the name is used to determine whether + an attribute has been specified. */ +typedef struct attribute_id { + XML_Char *name; + PREFIX *prefix; + XML_Bool maybeTokenized; + XML_Bool xmlns; +} ATTRIBUTE_ID; + +typedef struct { + const ATTRIBUTE_ID *id; + XML_Bool isCdata; + const XML_Char *value; +} DEFAULT_ATTRIBUTE; + +typedef struct { + unsigned long version; + unsigned long hash; + const XML_Char *uriName; +} NS_ATT; + +typedef struct { + const XML_Char *name; + PREFIX *prefix; + const ATTRIBUTE_ID *idAtt; + int nDefaultAtts; + int allocDefaultAtts; + DEFAULT_ATTRIBUTE *defaultAtts; +} ELEMENT_TYPE; + +typedef struct { + HASH_TABLE generalEntities; + HASH_TABLE elementTypes; + HASH_TABLE attributeIds; + HASH_TABLE prefixes; + STRING_POOL pool; + STRING_POOL entityValuePool; + /* false once a parameter entity reference has been skipped */ + XML_Bool keepProcessing; + /* true once an internal or external PE reference has been encountered; + this includes the reference to an external subset */ + XML_Bool hasParamEntityRefs; + XML_Bool standalone; +#ifdef XML_DTD + /* indicates if external PE has been read */ + XML_Bool paramEntityRead; + HASH_TABLE paramEntities; +#endif /* XML_DTD */ + PREFIX defaultPrefix; + /* === scaffolding for building content model === */ + XML_Bool in_eldecl; + CONTENT_SCAFFOLD *scaffold; + unsigned contentStringLen; + unsigned scaffSize; + unsigned scaffCount; + int scaffLevel; + int *scaffIndex; +} DTD; + +typedef struct open_internal_entity { + const char *internalEventPtr; + const char *internalEventEndPtr; + struct open_internal_entity *next; + ENTITY *entity; + int startTagLevel; + XML_Bool betweenDecl; /* WFC: PE Between Declarations */ +} OPEN_INTERNAL_ENTITY; + +typedef enum XML_Error PTRCALL Processor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr); + +static Processor prologProcessor; +static Processor prologInitProcessor; +static Processor contentProcessor; +static Processor cdataSectionProcessor; +#ifdef XML_DTD +static Processor ignoreSectionProcessor; +static Processor externalParEntProcessor; +static Processor externalParEntInitProcessor; +static Processor entityValueProcessor; +static Processor entityValueInitProcessor; +#endif /* XML_DTD */ +static Processor epilogProcessor; +static Processor errorProcessor; +static Processor externalEntityInitProcessor; +static Processor externalEntityInitProcessor2; +static Processor externalEntityInitProcessor3; +static Processor externalEntityContentProcessor; +static Processor internalEntityProcessor; + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName); +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next); +static enum XML_Error +initializeEncoding(XML_Parser parser); +static enum XML_Error +doProlog(XML_Parser parser, const ENCODING *enc, const char *s, + const char *end, int tok, const char *next, const char **nextPtr, + XML_Bool haveMore); +static enum XML_Error +processInternalEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl); +static enum XML_Error +doContent(XML_Parser parser, int startTagLevel, const ENCODING *enc, + const char *start, const char *end, const char **endPtr, + XML_Bool haveMore); +static enum XML_Error +doCdataSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore); +#ifdef XML_DTD +static enum XML_Error +doIgnoreSection(XML_Parser parser, const ENCODING *, const char **startPtr, + const char *end, const char **nextPtr, XML_Bool haveMore); +#endif /* XML_DTD */ + +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *, const char *s, + TAG_NAME *tagNamePtr, BINDING **bindingsPtr); +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr); +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *, XML_Bool isCdata, + XML_Bool isId, const XML_Char *dfltValue, XML_Parser parser); +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *, XML_Bool isCdata, + const char *, const char *, STRING_POOL *); +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *); +static enum XML_Error +storeEntityValue(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end); +static int +reportComment(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); +static void +reportDefault(XML_Parser parser, const ENCODING *enc, const char *start, + const char *end); + +static const XML_Char * getContext(XML_Parser parser); +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context); + +static void FASTCALL normalizePublicId(XML_Char *s); + +static DTD * dtdCreate(const XML_Memory_Handling_Suite *ms); +/* do not call if parentParser != NULL */ +static void dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms); +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms); +static int +dtdCopy(XML_Parser oldParser, + DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms); +static int +copyEntityTable(XML_Parser oldParser, + HASH_TABLE *, STRING_POOL *, const HASH_TABLE *); +static NAMED * +lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize); +static void FASTCALL +hashTableInit(HASH_TABLE *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL hashTableClear(HASH_TABLE *); +static void FASTCALL hashTableDestroy(HASH_TABLE *); +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *, const HASH_TABLE *); +static NAMED * FASTCALL hashTableIterNext(HASH_TABLE_ITER *); + +static void FASTCALL +poolInit(STRING_POOL *, const XML_Memory_Handling_Suite *ms); +static void FASTCALL poolClear(STRING_POOL *); +static void FASTCALL poolDestroy(STRING_POOL *); +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end); +static XML_Bool FASTCALL poolGrow(STRING_POOL *pool); +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s); +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n); +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s); + +static int FASTCALL nextScaffoldPart(XML_Parser parser); +static XML_Content * build_model(XML_Parser parser); +static ELEMENT_TYPE * +getElementType(XML_Parser parser, const ENCODING *enc, + const char *ptr, const char *end); + +static unsigned long generate_hash_secret_salt(void); +static XML_Bool startParsing(XML_Parser parser); + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd); + +static void +parserInit(XML_Parser parser, const XML_Char *encodingName); + +#define poolStart(pool) ((pool)->start) +#define poolEnd(pool) ((pool)->ptr) +#define poolLength(pool) ((pool)->ptr - (pool)->start) +#define poolChop(pool) ((void)--(pool->ptr)) +#define poolLastChar(pool) (((pool)->ptr)[-1]) +#define poolDiscard(pool) ((pool)->ptr = (pool)->start) +#define poolFinish(pool) ((pool)->start = (pool)->ptr) +#define poolAppendChar(pool, c) \ + (((pool)->ptr == (pool)->end && !poolGrow(pool)) \ + ? 0 \ + : ((*((pool)->ptr)++ = c), 1)) + +struct XML_ParserStruct { + /* The first member must be userData so that the XML_GetUserData + macro works. */ + void *m_userData; + void *m_handlerArg; + char *m_buffer; + const XML_Memory_Handling_Suite m_mem; + /* first character to be parsed */ + const char *m_bufferPtr; + /* past last character to be parsed */ + char *m_bufferEnd; + /* allocated end of buffer */ + const char *m_bufferLim; + XML_Index m_parseEndByteIndex; + const char *m_parseEndPtr; + XML_Char *m_dataBuf; + XML_Char *m_dataBufEnd; + XML_StartElementHandler m_startElementHandler; + XML_EndElementHandler m_endElementHandler; + XML_CharacterDataHandler m_characterDataHandler; + XML_ProcessingInstructionHandler m_processingInstructionHandler; + XML_CommentHandler m_commentHandler; + XML_StartCdataSectionHandler m_startCdataSectionHandler; + XML_EndCdataSectionHandler m_endCdataSectionHandler; + XML_DefaultHandler m_defaultHandler; + XML_StartDoctypeDeclHandler m_startDoctypeDeclHandler; + XML_EndDoctypeDeclHandler m_endDoctypeDeclHandler; + XML_UnparsedEntityDeclHandler m_unparsedEntityDeclHandler; + XML_NotationDeclHandler m_notationDeclHandler; + XML_StartNamespaceDeclHandler m_startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler m_endNamespaceDeclHandler; + XML_NotStandaloneHandler m_notStandaloneHandler; + XML_ExternalEntityRefHandler m_externalEntityRefHandler; + XML_Parser m_externalEntityRefHandlerArg; + XML_SkippedEntityHandler m_skippedEntityHandler; + XML_UnknownEncodingHandler m_unknownEncodingHandler; + XML_ElementDeclHandler m_elementDeclHandler; + XML_AttlistDeclHandler m_attlistDeclHandler; + XML_EntityDeclHandler m_entityDeclHandler; + XML_XmlDeclHandler m_xmlDeclHandler; + const ENCODING *m_encoding; + INIT_ENCODING m_initEncoding; + const ENCODING *m_internalEncoding; + const XML_Char *m_protocolEncodingName; + XML_Bool m_ns; + XML_Bool m_ns_triplets; + void *m_unknownEncodingMem; + void *m_unknownEncodingData; + void *m_unknownEncodingHandlerData; + void (XMLCALL *m_unknownEncodingRelease)(void *); + PROLOG_STATE m_prologState; + Processor *m_processor; + enum XML_Error m_errorCode; + const char *m_eventPtr; + const char *m_eventEndPtr; + const char *m_positionPtr; + OPEN_INTERNAL_ENTITY *m_openInternalEntities; + OPEN_INTERNAL_ENTITY *m_freeInternalEntities; + XML_Bool m_defaultExpandInternalEntities; + int m_tagLevel; + ENTITY *m_declEntity; + const XML_Char *m_doctypeName; + const XML_Char *m_doctypeSysid; + const XML_Char *m_doctypePubid; + const XML_Char *m_declAttributeType; + const XML_Char *m_declNotationName; + const XML_Char *m_declNotationPublicId; + ELEMENT_TYPE *m_declElementType; + ATTRIBUTE_ID *m_declAttributeId; + XML_Bool m_declAttributeIsCdata; + XML_Bool m_declAttributeIsId; + DTD *m_dtd; + const XML_Char *m_curBase; + TAG *m_tagStack; + TAG *m_freeTagList; + BINDING *m_inheritedBindings; + BINDING *m_freeBindingList; + int m_attsSize; + int m_nSpecifiedAtts; + int m_idAttIndex; + ATTRIBUTE *m_atts; + NS_ATT *m_nsAtts; + unsigned long m_nsAttsVersion; + unsigned char m_nsAttsPower; +#ifdef XML_ATTR_INFO + XML_AttrInfo *m_attInfo; +#endif + POSITION m_position; + STRING_POOL m_tempPool; + STRING_POOL m_temp2Pool; + char *m_groupConnector; + unsigned int m_groupSize; + XML_Char m_namespaceSeparator; + XML_Parser m_parentParser; + XML_ParsingStatus m_parsingStatus; +#ifdef XML_DTD + XML_Bool m_isParamEntity; + XML_Bool m_useForeignDTD; + enum XML_ParamEntityParsing m_paramEntityParsing; +#endif + unsigned long m_hash_secret_salt; +}; + +#define MALLOC(s) (parser->m_mem.malloc_fcn((s))) +#define REALLOC(p,s) (parser->m_mem.realloc_fcn((p),(s))) +#define FREE(p) (parser->m_mem.free_fcn((p))) + +#define userData (parser->m_userData) +#define handlerArg (parser->m_handlerArg) +#define startElementHandler (parser->m_startElementHandler) +#define endElementHandler (parser->m_endElementHandler) +#define characterDataHandler (parser->m_characterDataHandler) +#define processingInstructionHandler \ + (parser->m_processingInstructionHandler) +#define commentHandler (parser->m_commentHandler) +#define startCdataSectionHandler \ + (parser->m_startCdataSectionHandler) +#define endCdataSectionHandler (parser->m_endCdataSectionHandler) +#define defaultHandler (parser->m_defaultHandler) +#define startDoctypeDeclHandler (parser->m_startDoctypeDeclHandler) +#define endDoctypeDeclHandler (parser->m_endDoctypeDeclHandler) +#define unparsedEntityDeclHandler \ + (parser->m_unparsedEntityDeclHandler) +#define notationDeclHandler (parser->m_notationDeclHandler) +#define startNamespaceDeclHandler \ + (parser->m_startNamespaceDeclHandler) +#define endNamespaceDeclHandler (parser->m_endNamespaceDeclHandler) +#define notStandaloneHandler (parser->m_notStandaloneHandler) +#define externalEntityRefHandler \ + (parser->m_externalEntityRefHandler) +#define externalEntityRefHandlerArg \ + (parser->m_externalEntityRefHandlerArg) +#define internalEntityRefHandler \ + (parser->m_internalEntityRefHandler) +#define skippedEntityHandler (parser->m_skippedEntityHandler) +#define unknownEncodingHandler (parser->m_unknownEncodingHandler) +#define elementDeclHandler (parser->m_elementDeclHandler) +#define attlistDeclHandler (parser->m_attlistDeclHandler) +#define entityDeclHandler (parser->m_entityDeclHandler) +#define xmlDeclHandler (parser->m_xmlDeclHandler) +#define encoding (parser->m_encoding) +#define initEncoding (parser->m_initEncoding) +#define internalEncoding (parser->m_internalEncoding) +#define unknownEncodingMem (parser->m_unknownEncodingMem) +#define unknownEncodingData (parser->m_unknownEncodingData) +#define unknownEncodingHandlerData \ + (parser->m_unknownEncodingHandlerData) +#define unknownEncodingRelease (parser->m_unknownEncodingRelease) +#define protocolEncodingName (parser->m_protocolEncodingName) +#define ns (parser->m_ns) +#define ns_triplets (parser->m_ns_triplets) +#define prologState (parser->m_prologState) +#define processor (parser->m_processor) +#define errorCode (parser->m_errorCode) +#define eventPtr (parser->m_eventPtr) +#define eventEndPtr (parser->m_eventEndPtr) +#define positionPtr (parser->m_positionPtr) +#define position (parser->m_position) +#define openInternalEntities (parser->m_openInternalEntities) +#define freeInternalEntities (parser->m_freeInternalEntities) +#define defaultExpandInternalEntities \ + (parser->m_defaultExpandInternalEntities) +#define tagLevel (parser->m_tagLevel) +#define buffer (parser->m_buffer) +#define bufferPtr (parser->m_bufferPtr) +#define bufferEnd (parser->m_bufferEnd) +#define parseEndByteIndex (parser->m_parseEndByteIndex) +#define parseEndPtr (parser->m_parseEndPtr) +#define bufferLim (parser->m_bufferLim) +#define dataBuf (parser->m_dataBuf) +#define dataBufEnd (parser->m_dataBufEnd) +#define _dtd (parser->m_dtd) +#define curBase (parser->m_curBase) +#define declEntity (parser->m_declEntity) +#define doctypeName (parser->m_doctypeName) +#define doctypeSysid (parser->m_doctypeSysid) +#define doctypePubid (parser->m_doctypePubid) +#define declAttributeType (parser->m_declAttributeType) +#define declNotationName (parser->m_declNotationName) +#define declNotationPublicId (parser->m_declNotationPublicId) +#define declElementType (parser->m_declElementType) +#define declAttributeId (parser->m_declAttributeId) +#define declAttributeIsCdata (parser->m_declAttributeIsCdata) +#define declAttributeIsId (parser->m_declAttributeIsId) +#define freeTagList (parser->m_freeTagList) +#define freeBindingList (parser->m_freeBindingList) +#define inheritedBindings (parser->m_inheritedBindings) +#define tagStack (parser->m_tagStack) +#define atts (parser->m_atts) +#define attsSize (parser->m_attsSize) +#define nSpecifiedAtts (parser->m_nSpecifiedAtts) +#define idAttIndex (parser->m_idAttIndex) +#define nsAtts (parser->m_nsAtts) +#define nsAttsVersion (parser->m_nsAttsVersion) +#define nsAttsPower (parser->m_nsAttsPower) +#define attInfo (parser->m_attInfo) +#define tempPool (parser->m_tempPool) +#define temp2Pool (parser->m_temp2Pool) +#define groupConnector (parser->m_groupConnector) +#define groupSize (parser->m_groupSize) +#define namespaceSeparator (parser->m_namespaceSeparator) +#define parentParser (parser->m_parentParser) +#define ps_parsing (parser->m_parsingStatus.parsing) +#define ps_finalBuffer (parser->m_parsingStatus.finalBuffer) +#ifdef XML_DTD +#define isParamEntity (parser->m_isParamEntity) +#define useForeignDTD (parser->m_useForeignDTD) +#define paramEntityParsing (parser->m_paramEntityParsing) +#endif /* XML_DTD */ +#define hash_secret_salt (parser->m_hash_secret_salt) + +XML_Parser XMLCALL +XML_ParserCreate(const XML_Char *encodingName) +{ + return XML_ParserCreate_MM(encodingName, NULL, NULL); +} + +XML_Parser XMLCALL +XML_ParserCreateNS(const XML_Char *encodingName, XML_Char nsSep) +{ + XML_Char tmp[2]; + *tmp = nsSep; + return XML_ParserCreate_MM(encodingName, NULL, tmp); +} + +static const XML_Char implicitContext[] = { + ASCII_x, ASCII_m, ASCII_l, ASCII_EQUALS, ASCII_h, ASCII_t, ASCII_t, ASCII_p, + ASCII_COLON, ASCII_SLASH, ASCII_SLASH, ASCII_w, ASCII_w, ASCII_w, + ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, ASCII_o, ASCII_r, ASCII_g, + ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, ASCII_SLASH, ASCII_1, ASCII_9, + ASCII_9, ASCII_8, ASCII_SLASH, ASCII_n, ASCII_a, ASCII_m, ASCII_e, + ASCII_s, ASCII_p, ASCII_a, ASCII_c, ASCII_e, '\0' +}; + +static unsigned long +generate_hash_secret_salt(void) +{ + unsigned int seed = time(NULL) % UINT_MAX; + srand(seed); + return rand(); +} + +static XML_Bool /* only valid for root parser */ +startParsing(XML_Parser parser) +{ + /* hash functions must be initialized before setContext() is called */ + if (hash_secret_salt == 0) + hash_secret_salt = generate_hash_secret_salt(); + if (ns) { + /* implicit context only set for root parser, since child + parsers (i.e. external entity parsers) will inherit it + */ + return setContext(parser, implicitContext); + } + return XML_TRUE; +} + +XML_Parser XMLCALL +XML_ParserCreate_MM(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep) +{ + return parserCreate(encodingName, memsuite, nameSep, NULL); +} + +static XML_Parser +parserCreate(const XML_Char *encodingName, + const XML_Memory_Handling_Suite *memsuite, + const XML_Char *nameSep, + DTD *dtd) +{ + XML_Parser parser; + + if (memsuite) { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser) + memsuite->malloc_fcn(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = memsuite->malloc_fcn; + mtemp->realloc_fcn = memsuite->realloc_fcn; + mtemp->free_fcn = memsuite->free_fcn; + } + } + else { + XML_Memory_Handling_Suite *mtemp; + parser = (XML_Parser)malloc(sizeof(struct XML_ParserStruct)); + if (parser != NULL) { + mtemp = (XML_Memory_Handling_Suite *)&(parser->m_mem); + mtemp->malloc_fcn = malloc; + mtemp->realloc_fcn = realloc; + mtemp->free_fcn = free; + } + } + + if (!parser) + return parser; + + buffer = NULL; + bufferLim = NULL; + + attsSize = INIT_ATTS_SIZE; + atts = (ATTRIBUTE *)MALLOC(attsSize * sizeof(ATTRIBUTE)); + if (atts == NULL) { + FREE(parser); + return NULL; + } +#ifdef XML_ATTR_INFO + attInfo = (XML_AttrInfo*)MALLOC(attsSize * sizeof(XML_AttrInfo)); + if (attInfo == NULL) { + FREE(atts); + FREE(parser); + return NULL; + } +#endif + dataBuf = (XML_Char *)MALLOC(INIT_DATA_BUF_SIZE * sizeof(XML_Char)); + if (dataBuf == NULL) { + FREE(atts); +#ifdef XML_ATTR_INFO + FREE(attInfo); +#endif + FREE(parser); + return NULL; + } + dataBufEnd = dataBuf + INIT_DATA_BUF_SIZE; + + if (dtd) + _dtd = dtd; + else { + _dtd = dtdCreate(&parser->m_mem); + if (_dtd == NULL) { + FREE(dataBuf); + FREE(atts); +#ifdef XML_ATTR_INFO + FREE(attInfo); +#endif + FREE(parser); + return NULL; + } + } + + freeBindingList = NULL; + freeTagList = NULL; + freeInternalEntities = NULL; + + groupSize = 0; + groupConnector = NULL; + + unknownEncodingHandler = NULL; + unknownEncodingHandlerData = NULL; + + namespaceSeparator = ASCII_EXCL; + ns = XML_FALSE; + ns_triplets = XML_FALSE; + + nsAtts = NULL; + nsAttsVersion = 0; + nsAttsPower = 0; + + poolInit(&tempPool, &(parser->m_mem)); + poolInit(&temp2Pool, &(parser->m_mem)); + parserInit(parser, encodingName); + + if (encodingName && !protocolEncodingName) { + XML_ParserFree(parser); + return NULL; + } + + if (nameSep) { + ns = XML_TRUE; + internalEncoding = XmlGetInternalEncodingNS(); + namespaceSeparator = *nameSep; + } + else { + internalEncoding = XmlGetInternalEncoding(); + } + + return parser; +} + +static void +parserInit(XML_Parser parser, const XML_Char *encodingName) +{ + processor = prologInitProcessor; + XmlPrologStateInit(&prologState); + protocolEncodingName = (encodingName != NULL + ? poolCopyString(&tempPool, encodingName) + : NULL); + curBase = NULL; + XmlInitEncoding(&initEncoding, &encoding, 0); + userData = NULL; + handlerArg = NULL; + startElementHandler = NULL; + endElementHandler = NULL; + characterDataHandler = NULL; + processingInstructionHandler = NULL; + commentHandler = NULL; + startCdataSectionHandler = NULL; + endCdataSectionHandler = NULL; + defaultHandler = NULL; + startDoctypeDeclHandler = NULL; + endDoctypeDeclHandler = NULL; + unparsedEntityDeclHandler = NULL; + notationDeclHandler = NULL; + startNamespaceDeclHandler = NULL; + endNamespaceDeclHandler = NULL; + notStandaloneHandler = NULL; + externalEntityRefHandler = NULL; + externalEntityRefHandlerArg = parser; + skippedEntityHandler = NULL; + elementDeclHandler = NULL; + attlistDeclHandler = NULL; + entityDeclHandler = NULL; + xmlDeclHandler = NULL; + bufferPtr = buffer; + bufferEnd = buffer; + parseEndByteIndex = 0; + parseEndPtr = NULL; + declElementType = NULL; + declAttributeId = NULL; + declEntity = NULL; + doctypeName = NULL; + doctypeSysid = NULL; + doctypePubid = NULL; + declAttributeType = NULL; + declNotationName = NULL; + declNotationPublicId = NULL; + declAttributeIsCdata = XML_FALSE; + declAttributeIsId = XML_FALSE; + memset(&position, 0, sizeof(POSITION)); + errorCode = XML_ERROR_NONE; + eventPtr = NULL; + eventEndPtr = NULL; + positionPtr = NULL; + openInternalEntities = NULL; + defaultExpandInternalEntities = XML_TRUE; + tagLevel = 0; + tagStack = NULL; + inheritedBindings = NULL; + nSpecifiedAtts = 0; + unknownEncodingMem = NULL; + unknownEncodingRelease = NULL; + unknownEncodingData = NULL; + parentParser = NULL; + ps_parsing = XML_INITIALIZED; +#ifdef XML_DTD + isParamEntity = XML_FALSE; + useForeignDTD = XML_FALSE; + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif + hash_secret_salt = 0; +} + +/* moves list of bindings to freeBindingList */ +static void FASTCALL +moveToFreeBindingList(XML_Parser parser, BINDING *bindings) +{ + while (bindings) { + BINDING *b = bindings; + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + } +} + +XML_Bool XMLCALL +XML_ParserReset(XML_Parser parser, const XML_Char *encodingName) +{ + TAG *tStk; + OPEN_INTERNAL_ENTITY *openEntityList; + if (parentParser) + return XML_FALSE; + /* move tagStack to freeTagList */ + tStk = tagStack; + while (tStk) { + TAG *tag = tStk; + tStk = tStk->parent; + tag->parent = freeTagList; + moveToFreeBindingList(parser, tag->bindings); + tag->bindings = NULL; + freeTagList = tag; + } + /* move openInternalEntities to freeInternalEntities */ + openEntityList = openInternalEntities; + while (openEntityList) { + OPEN_INTERNAL_ENTITY *openEntity = openEntityList; + openEntityList = openEntity->next; + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + moveToFreeBindingList(parser, inheritedBindings); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + poolClear(&tempPool); + poolClear(&temp2Pool); + parserInit(parser, encodingName); + dtdReset(_dtd, &parser->m_mem); + return XML_TRUE; +} + +enum XML_Status XMLCALL +XML_SetEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + /* Block after XML_Parse()/XML_ParseBuffer() has been called. + XXX There's no way for the caller to determine which of the + XXX possible error cases caused the XML_STATUS_ERROR return. + */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return XML_STATUS_ERROR; + if (encodingName == NULL) + protocolEncodingName = NULL; + else { + protocolEncodingName = poolCopyString(&tempPool, encodingName); + if (!protocolEncodingName) + return XML_STATUS_ERROR; + } + return XML_STATUS_OK; +} + +XML_Parser XMLCALL +XML_ExternalEntityParserCreate(XML_Parser oldParser, + const XML_Char *context, + const XML_Char *encodingName) +{ + XML_Parser parser = oldParser; + DTD *newDtd = NULL; + DTD *oldDtd = _dtd; + XML_StartElementHandler oldStartElementHandler = startElementHandler; + XML_EndElementHandler oldEndElementHandler = endElementHandler; + XML_CharacterDataHandler oldCharacterDataHandler = characterDataHandler; + XML_ProcessingInstructionHandler oldProcessingInstructionHandler + = processingInstructionHandler; + XML_CommentHandler oldCommentHandler = commentHandler; + XML_StartCdataSectionHandler oldStartCdataSectionHandler + = startCdataSectionHandler; + XML_EndCdataSectionHandler oldEndCdataSectionHandler + = endCdataSectionHandler; + XML_DefaultHandler oldDefaultHandler = defaultHandler; + XML_UnparsedEntityDeclHandler oldUnparsedEntityDeclHandler + = unparsedEntityDeclHandler; + XML_NotationDeclHandler oldNotationDeclHandler = notationDeclHandler; + XML_StartNamespaceDeclHandler oldStartNamespaceDeclHandler + = startNamespaceDeclHandler; + XML_EndNamespaceDeclHandler oldEndNamespaceDeclHandler + = endNamespaceDeclHandler; + XML_NotStandaloneHandler oldNotStandaloneHandler = notStandaloneHandler; + XML_ExternalEntityRefHandler oldExternalEntityRefHandler + = externalEntityRefHandler; + XML_SkippedEntityHandler oldSkippedEntityHandler = skippedEntityHandler; + XML_UnknownEncodingHandler oldUnknownEncodingHandler + = unknownEncodingHandler; + XML_ElementDeclHandler oldElementDeclHandler = elementDeclHandler; + XML_AttlistDeclHandler oldAttlistDeclHandler = attlistDeclHandler; + XML_EntityDeclHandler oldEntityDeclHandler = entityDeclHandler; + XML_XmlDeclHandler oldXmlDeclHandler = xmlDeclHandler; + ELEMENT_TYPE * oldDeclElementType = declElementType; + + void *oldUserData = userData; + void *oldHandlerArg = handlerArg; + XML_Bool oldDefaultExpandInternalEntities = defaultExpandInternalEntities; + XML_Parser oldExternalEntityRefHandlerArg = externalEntityRefHandlerArg; +#ifdef XML_DTD + enum XML_ParamEntityParsing oldParamEntityParsing = paramEntityParsing; + int oldInEntityValue = prologState.inEntityValue; +#endif + XML_Bool oldns_triplets = ns_triplets; + /* Note that the new parser shares the same hash secret as the old + parser, so that dtdCopy and copyEntityTable can lookup values + from hash tables associated with either parser without us having + to worry which hash secrets each table has. + */ + unsigned long oldhash_secret_salt = hash_secret_salt; + +#ifdef XML_DTD + if (!context) + newDtd = oldDtd; +#endif /* XML_DTD */ + + /* Note that the magical uses of the pre-processor to make field + access look more like C++ require that `parser' be overwritten + here. This makes this function more painful to follow than it + would be otherwise. + */ + if (ns) { + XML_Char tmp[2]; + *tmp = namespaceSeparator; + parser = parserCreate(encodingName, &parser->m_mem, tmp, newDtd); + } + else { + parser = parserCreate(encodingName, &parser->m_mem, NULL, newDtd); + } + + if (!parser) + return NULL; + + startElementHandler = oldStartElementHandler; + endElementHandler = oldEndElementHandler; + characterDataHandler = oldCharacterDataHandler; + processingInstructionHandler = oldProcessingInstructionHandler; + commentHandler = oldCommentHandler; + startCdataSectionHandler = oldStartCdataSectionHandler; + endCdataSectionHandler = oldEndCdataSectionHandler; + defaultHandler = oldDefaultHandler; + unparsedEntityDeclHandler = oldUnparsedEntityDeclHandler; + notationDeclHandler = oldNotationDeclHandler; + startNamespaceDeclHandler = oldStartNamespaceDeclHandler; + endNamespaceDeclHandler = oldEndNamespaceDeclHandler; + notStandaloneHandler = oldNotStandaloneHandler; + externalEntityRefHandler = oldExternalEntityRefHandler; + skippedEntityHandler = oldSkippedEntityHandler; + unknownEncodingHandler = oldUnknownEncodingHandler; + elementDeclHandler = oldElementDeclHandler; + attlistDeclHandler = oldAttlistDeclHandler; + entityDeclHandler = oldEntityDeclHandler; + xmlDeclHandler = oldXmlDeclHandler; + declElementType = oldDeclElementType; + userData = oldUserData; + if (oldUserData == oldHandlerArg) + handlerArg = userData; + else + handlerArg = parser; + if (oldExternalEntityRefHandlerArg != oldParser) + externalEntityRefHandlerArg = oldExternalEntityRefHandlerArg; + defaultExpandInternalEntities = oldDefaultExpandInternalEntities; + ns_triplets = oldns_triplets; + hash_secret_salt = oldhash_secret_salt; + parentParser = oldParser; +#ifdef XML_DTD + paramEntityParsing = oldParamEntityParsing; + prologState.inEntityValue = oldInEntityValue; + if (context) { +#endif /* XML_DTD */ + if (!dtdCopy(oldParser, _dtd, oldDtd, &parser->m_mem) + || !setContext(parser, context)) { + XML_ParserFree(parser); + return NULL; + } + processor = externalEntityInitProcessor; +#ifdef XML_DTD + } + else { + /* The DTD instance referenced by _dtd is shared between the document's + root parser and external PE parsers, therefore one does not need to + call setContext. In addition, one also *must* not call setContext, + because this would overwrite existing prefix->binding pointers in + _dtd with ones that get destroyed with the external PE parser. + This would leave those prefixes with dangling pointers. + */ + isParamEntity = XML_TRUE; + XmlPrologStateInitExternalEntity(&prologState); + processor = externalParEntInitProcessor; + } +#endif /* XML_DTD */ + return parser; +} + +static void FASTCALL +destroyBindings(BINDING *bindings, XML_Parser parser) +{ + for (;;) { + BINDING *b = bindings; + if (!b) + break; + bindings = b->nextTagBinding; + FREE(b->uri); + FREE(b); + } +} + +void XMLCALL +XML_ParserFree(XML_Parser parser) +{ + TAG *tagList; + OPEN_INTERNAL_ENTITY *entityList; + if (parser == NULL) + return; + /* free tagStack and freeTagList */ + tagList = tagStack; + for (;;) { + TAG *p; + if (tagList == NULL) { + if (freeTagList == NULL) + break; + tagList = freeTagList; + freeTagList = NULL; + } + p = tagList; + tagList = tagList->parent; + FREE(p->buf); + destroyBindings(p->bindings, parser); + FREE(p); + } + /* free openInternalEntities and freeInternalEntities */ + entityList = openInternalEntities; + for (;;) { + OPEN_INTERNAL_ENTITY *openEntity; + if (entityList == NULL) { + if (freeInternalEntities == NULL) + break; + entityList = freeInternalEntities; + freeInternalEntities = NULL; + } + openEntity = entityList; + entityList = entityList->next; + FREE(openEntity); + } + + destroyBindings(freeBindingList, parser); + destroyBindings(inheritedBindings, parser); + poolDestroy(&tempPool); + poolDestroy(&temp2Pool); +#ifdef XML_DTD + /* external parameter entity parsers share the DTD structure + parser->m_dtd with the root parser, so we must not destroy it + */ + if (!isParamEntity && _dtd) +#else + if (_dtd) +#endif /* XML_DTD */ + dtdDestroy(_dtd, (XML_Bool)!parentParser, &parser->m_mem); + FREE((void *)atts); +#ifdef XML_ATTR_INFO + FREE((void *)attInfo); +#endif + FREE(groupConnector); + FREE(buffer); + FREE(dataBuf); + FREE(nsAtts); + FREE(unknownEncodingMem); + if (unknownEncodingRelease) + unknownEncodingRelease(unknownEncodingData); + FREE(parser); +} + +void XMLCALL +XML_UseParserAsHandlerArg(XML_Parser parser) +{ + handlerArg = parser; +} + +enum XML_Error XMLCALL +XML_UseForeignDTD(XML_Parser parser, XML_Bool useDTD) +{ +#ifdef XML_DTD + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return XML_ERROR_CANT_CHANGE_FEATURE_ONCE_PARSING; + useForeignDTD = useDTD; + return XML_ERROR_NONE; +#else + return XML_ERROR_FEATURE_REQUIRES_XML_DTD; +#endif +} + +void XMLCALL +XML_SetReturnNSTriplet(XML_Parser parser, int do_nst) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return; + ns_triplets = do_nst ? XML_TRUE : XML_FALSE; +} + +void XMLCALL +XML_SetUserData(XML_Parser parser, void *p) +{ + if (handlerArg == userData) + handlerArg = userData = p; + else + userData = p; +} + +enum XML_Status XMLCALL +XML_SetBase(XML_Parser parser, const XML_Char *p) +{ + if (p) { + p = poolCopyString(&_dtd->pool, p); + if (!p) + return XML_STATUS_ERROR; + curBase = p; + } + else + curBase = NULL; + return XML_STATUS_OK; +} + +const XML_Char * XMLCALL +XML_GetBase(XML_Parser parser) +{ + return curBase; +} + +int XMLCALL +XML_GetSpecifiedAttributeCount(XML_Parser parser) +{ + return nSpecifiedAtts; +} + +int XMLCALL +XML_GetIdAttributeIndex(XML_Parser parser) +{ + return idAttIndex; +} + +#ifdef XML_ATTR_INFO +const XML_AttrInfo * XMLCALL +XML_GetAttributeInfo(XML_Parser parser) +{ + return attInfo; +} +#endif + +void XMLCALL +XML_SetElementHandler(XML_Parser parser, + XML_StartElementHandler start, + XML_EndElementHandler end) +{ + startElementHandler = start; + endElementHandler = end; +} + +void XMLCALL +XML_SetStartElementHandler(XML_Parser parser, + XML_StartElementHandler start) { + startElementHandler = start; +} + +void XMLCALL +XML_SetEndElementHandler(XML_Parser parser, + XML_EndElementHandler end) { + endElementHandler = end; +} + +void XMLCALL +XML_SetCharacterDataHandler(XML_Parser parser, + XML_CharacterDataHandler handler) +{ + characterDataHandler = handler; +} + +void XMLCALL +XML_SetProcessingInstructionHandler(XML_Parser parser, + XML_ProcessingInstructionHandler handler) +{ + processingInstructionHandler = handler; +} + +void XMLCALL +XML_SetCommentHandler(XML_Parser parser, + XML_CommentHandler handler) +{ + commentHandler = handler; +} + +void XMLCALL +XML_SetCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start, + XML_EndCdataSectionHandler end) +{ + startCdataSectionHandler = start; + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetStartCdataSectionHandler(XML_Parser parser, + XML_StartCdataSectionHandler start) { + startCdataSectionHandler = start; +} + +void XMLCALL +XML_SetEndCdataSectionHandler(XML_Parser parser, + XML_EndCdataSectionHandler end) { + endCdataSectionHandler = end; +} + +void XMLCALL +XML_SetDefaultHandler(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_FALSE; +} + +void XMLCALL +XML_SetDefaultHandlerExpand(XML_Parser parser, + XML_DefaultHandler handler) +{ + defaultHandler = handler; + defaultExpandInternalEntities = XML_TRUE; +} + +void XMLCALL +XML_SetDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start, + XML_EndDoctypeDeclHandler end) +{ + startDoctypeDeclHandler = start; + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetStartDoctypeDeclHandler(XML_Parser parser, + XML_StartDoctypeDeclHandler start) { + startDoctypeDeclHandler = start; +} + +void XMLCALL +XML_SetEndDoctypeDeclHandler(XML_Parser parser, + XML_EndDoctypeDeclHandler end) { + endDoctypeDeclHandler = end; +} + +void XMLCALL +XML_SetUnparsedEntityDeclHandler(XML_Parser parser, + XML_UnparsedEntityDeclHandler handler) +{ + unparsedEntityDeclHandler = handler; +} + +void XMLCALL +XML_SetNotationDeclHandler(XML_Parser parser, + XML_NotationDeclHandler handler) +{ + notationDeclHandler = handler; +} + +void XMLCALL +XML_SetNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start, + XML_EndNamespaceDeclHandler end) +{ + startNamespaceDeclHandler = start; + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetStartNamespaceDeclHandler(XML_Parser parser, + XML_StartNamespaceDeclHandler start) { + startNamespaceDeclHandler = start; +} + +void XMLCALL +XML_SetEndNamespaceDeclHandler(XML_Parser parser, + XML_EndNamespaceDeclHandler end) { + endNamespaceDeclHandler = end; +} + +void XMLCALL +XML_SetNotStandaloneHandler(XML_Parser parser, + XML_NotStandaloneHandler handler) +{ + notStandaloneHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandler(XML_Parser parser, + XML_ExternalEntityRefHandler handler) +{ + externalEntityRefHandler = handler; +} + +void XMLCALL +XML_SetExternalEntityRefHandlerArg(XML_Parser parser, void *arg) +{ + if (arg) + externalEntityRefHandlerArg = (XML_Parser)arg; + else + externalEntityRefHandlerArg = parser; +} + +void XMLCALL +XML_SetSkippedEntityHandler(XML_Parser parser, + XML_SkippedEntityHandler handler) +{ + skippedEntityHandler = handler; +} + +void XMLCALL +XML_SetUnknownEncodingHandler(XML_Parser parser, + XML_UnknownEncodingHandler handler, + void *data) +{ + unknownEncodingHandler = handler; + unknownEncodingHandlerData = data; +} + +void XMLCALL +XML_SetElementDeclHandler(XML_Parser parser, + XML_ElementDeclHandler eldecl) +{ + elementDeclHandler = eldecl; +} + +void XMLCALL +XML_SetAttlistDeclHandler(XML_Parser parser, + XML_AttlistDeclHandler attdecl) +{ + attlistDeclHandler = attdecl; +} + +void XMLCALL +XML_SetEntityDeclHandler(XML_Parser parser, + XML_EntityDeclHandler handler) +{ + entityDeclHandler = handler; +} + +void XMLCALL +XML_SetXmlDeclHandler(XML_Parser parser, + XML_XmlDeclHandler handler) { + xmlDeclHandler = handler; +} + +int XMLCALL +XML_SetParamEntityParsing(XML_Parser parser, + enum XML_ParamEntityParsing peParsing) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return 0; +#ifdef XML_DTD + paramEntityParsing = peParsing; + return 1; +#else + return peParsing == XML_PARAM_ENTITY_PARSING_NEVER; +#endif +} + +int XMLCALL +XML_SetHashSalt(XML_Parser parser, + unsigned long hash_salt) +{ + /* block after XML_Parse()/XML_ParseBuffer() has been called */ + if (ps_parsing == XML_PARSING || ps_parsing == XML_SUSPENDED) + return 0; + hash_secret_salt = hash_salt; + return 1; +} + +enum XML_Status XMLCALL +XML_Parse(XML_Parser parser, const char *s, int len, int isFinal) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + case XML_INITIALIZED: + if (parentParser == NULL && !startParsing(parser)) { + errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + default: + ps_parsing = XML_PARSING; + } + + if (len == 0) { + ps_finalBuffer = (XML_Bool)isFinal; + if (!isFinal) + return XML_STATUS_OK; + positionPtr = bufferPtr; + parseEndPtr = bufferEnd; + + /* If data are left over from last buffer, and we now know that these + data are the final chunk of input, then we have to check them again + to detect errors based on that fact. + */ + errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + + if (errorCode == XML_ERROR_NONE) { + switch (ps_parsing) { + case XML_SUSPENDED: + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return XML_STATUS_SUSPENDED; + case XML_INITIALIZED: + case XML_PARSING: + ps_parsing = XML_FINISHED; + /* fall through */ + default: + return XML_STATUS_OK; + } + } + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } +#ifndef XML_CONTEXT_BYTES + else if (bufferPtr == bufferEnd) { + const char *end; + int nLeftOver; + enum XML_Error result; + parseEndByteIndex += len; + positionPtr = s; + ps_finalBuffer = (XML_Bool)isFinal; + + errorCode = processor(parser, s, parseEndPtr = s + len, &end); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (isFinal) { + ps_parsing = XML_FINISHED; + return XML_STATUS_OK; + } + /* fall through */ + default: + result = XML_STATUS_OK; + } + } + + XmlUpdatePosition(encoding, positionPtr, end, &position); + nLeftOver = s + len - end; + if (nLeftOver) { + if (buffer == NULL || nLeftOver > bufferLim - buffer) { + /* FIXME avoid integer overflow */ + char *temp; + temp = (buffer == NULL + ? (char *)MALLOC(len * 2) + : (char *)REALLOC(buffer, len * 2)); + if (temp == NULL) { + errorCode = XML_ERROR_NO_MEMORY; + eventPtr = eventEndPtr = NULL; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + buffer = temp; + bufferLim = buffer + len * 2; + } + memcpy(buffer, end, nLeftOver); + } + bufferPtr = buffer; + bufferEnd = buffer + nLeftOver; + positionPtr = bufferPtr; + parseEndPtr = bufferEnd; + eventPtr = bufferPtr; + eventEndPtr = bufferPtr; + return result; + } +#endif /* not defined XML_CONTEXT_BYTES */ + else { + void *buff = XML_GetBuffer(parser, len); + if (buff == NULL) + return XML_STATUS_ERROR; + else { + memcpy(buff, s, len); + return XML_ParseBuffer(parser, len, isFinal); + } + } +} + +enum XML_Status XMLCALL +XML_ParseBuffer(XML_Parser parser, int len, int isFinal) +{ + const char *start; + enum XML_Status result = XML_STATUS_OK; + + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + case XML_INITIALIZED: + if (parentParser == NULL && !startParsing(parser)) { + errorCode = XML_ERROR_NO_MEMORY; + return XML_STATUS_ERROR; + } + default: + ps_parsing = XML_PARSING; + } + + start = bufferPtr; + positionPtr = start; + bufferEnd += len; + parseEndPtr = bufferEnd; + parseEndByteIndex += len; + ps_finalBuffer = (XML_Bool)isFinal; + + errorCode = processor(parser, start, parseEndPtr, &bufferPtr); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (isFinal) { + ps_parsing = XML_FINISHED; + return result; + } + default: ; /* should not happen */ + } + } + + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return result; +} + +void * XMLCALL +XML_GetBuffer(XML_Parser parser, int len) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + errorCode = XML_ERROR_SUSPENDED; + return NULL; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return NULL; + default: ; + } + + if (len > bufferLim - bufferEnd) { + /* FIXME avoid integer overflow */ + int neededSize = len + (int)(bufferEnd - bufferPtr); +#ifdef XML_CONTEXT_BYTES + int keep = (int)(bufferPtr - buffer); + + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + neededSize += keep; +#endif /* defined XML_CONTEXT_BYTES */ + if (neededSize <= bufferLim - buffer) { +#ifdef XML_CONTEXT_BYTES + if (keep < bufferPtr - buffer) { + int offset = (int)(bufferPtr - buffer) - keep; + memmove(buffer, &buffer[offset], bufferEnd - bufferPtr + keep); + bufferEnd -= offset; + bufferPtr -= offset; + } +#else + memmove(buffer, bufferPtr, bufferEnd - bufferPtr); + bufferEnd = buffer + (bufferEnd - bufferPtr); + bufferPtr = buffer; +#endif /* not defined XML_CONTEXT_BYTES */ + } + else { + char *newBuf; + int bufferSize = (int)(bufferLim - bufferPtr); + if (bufferSize == 0) + bufferSize = INIT_BUFFER_SIZE; + do { + bufferSize *= 2; + } while (bufferSize < neededSize); + newBuf = (char *)MALLOC(bufferSize); + if (newBuf == 0) { + errorCode = XML_ERROR_NO_MEMORY; + return NULL; + } + bufferLim = newBuf + bufferSize; +#ifdef XML_CONTEXT_BYTES + if (bufferPtr) { + int keep = (int)(bufferPtr - buffer); + if (keep > XML_CONTEXT_BYTES) + keep = XML_CONTEXT_BYTES; + memcpy(newBuf, &bufferPtr[-keep], bufferEnd - bufferPtr + keep); + FREE(buffer); + buffer = newBuf; + bufferEnd = buffer + (bufferEnd - bufferPtr) + keep; + bufferPtr = buffer + keep; + } + else { + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; + } +#else + if (bufferPtr) { + memcpy(newBuf, bufferPtr, bufferEnd - bufferPtr); + FREE(buffer); + } + bufferEnd = newBuf + (bufferEnd - bufferPtr); + bufferPtr = buffer = newBuf; +#endif /* not defined XML_CONTEXT_BYTES */ + } + eventPtr = eventEndPtr = NULL; + positionPtr = NULL; + } + return bufferEnd; +} + +enum XML_Status XMLCALL +XML_StopParser(XML_Parser parser, XML_Bool resumable) +{ + switch (ps_parsing) { + case XML_SUSPENDED: + if (resumable) { + errorCode = XML_ERROR_SUSPENDED; + return XML_STATUS_ERROR; + } + ps_parsing = XML_FINISHED; + break; + case XML_FINISHED: + errorCode = XML_ERROR_FINISHED; + return XML_STATUS_ERROR; + default: + if (resumable) { +#ifdef XML_DTD + if (isParamEntity) { + errorCode = XML_ERROR_SUSPEND_PE; + return XML_STATUS_ERROR; + } +#endif + ps_parsing = XML_SUSPENDED; + } + else + ps_parsing = XML_FINISHED; + } + return XML_STATUS_OK; +} + +enum XML_Status XMLCALL +XML_ResumeParser(XML_Parser parser) +{ + enum XML_Status result = XML_STATUS_OK; + + if (ps_parsing != XML_SUSPENDED) { + errorCode = XML_ERROR_NOT_SUSPENDED; + return XML_STATUS_ERROR; + } + ps_parsing = XML_PARSING; + + errorCode = processor(parser, bufferPtr, parseEndPtr, &bufferPtr); + + if (errorCode != XML_ERROR_NONE) { + eventEndPtr = eventPtr; + processor = errorProcessor; + return XML_STATUS_ERROR; + } + else { + switch (ps_parsing) { + case XML_SUSPENDED: + result = XML_STATUS_SUSPENDED; + break; + case XML_INITIALIZED: + case XML_PARSING: + if (ps_finalBuffer) { + ps_parsing = XML_FINISHED; + return result; + } + default: ; + } + } + + XmlUpdatePosition(encoding, positionPtr, bufferPtr, &position); + positionPtr = bufferPtr; + return result; +} + +void XMLCALL +XML_GetParsingStatus(XML_Parser parser, XML_ParsingStatus *status) +{ + assert(status != NULL); + *status = parser->m_parsingStatus; +} + +enum XML_Error XMLCALL +XML_GetErrorCode(XML_Parser parser) +{ + return errorCode; +} + +XML_Index XMLCALL +XML_GetCurrentByteIndex(XML_Parser parser) +{ + if (eventPtr) + return parseEndByteIndex - (parseEndPtr - eventPtr); + return -1; +} + +int XMLCALL +XML_GetCurrentByteCount(XML_Parser parser) +{ + if (eventEndPtr && eventPtr) + return (int)(eventEndPtr - eventPtr); + return 0; +} + +const char * XMLCALL +XML_GetInputContext(XML_Parser parser, int *offset, int *size) +{ +#ifdef XML_CONTEXT_BYTES + if (eventPtr && buffer) { + *offset = (int)(eventPtr - buffer); + *size = (int)(bufferEnd - buffer); + return buffer; + } +#endif /* defined XML_CONTEXT_BYTES */ + return (char *) 0; +} + +XML_Size XMLCALL +XML_GetCurrentLineNumber(XML_Parser parser) +{ + if (eventPtr && eventPtr >= positionPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.lineNumber + 1; +} + +XML_Size XMLCALL +XML_GetCurrentColumnNumber(XML_Parser parser) +{ + if (eventPtr && eventPtr >= positionPtr) { + XmlUpdatePosition(encoding, positionPtr, eventPtr, &position); + positionPtr = eventPtr; + } + return position.columnNumber; +} + +void XMLCALL +XML_FreeContentModel(XML_Parser parser, XML_Content *model) +{ + FREE(model); +} + +void * XMLCALL +XML_MemMalloc(XML_Parser parser, size_t size) +{ + return MALLOC(size); +} + +void * XMLCALL +XML_MemRealloc(XML_Parser parser, void *ptr, size_t size) +{ + return REALLOC(ptr, size); +} + +void XMLCALL +XML_MemFree(XML_Parser parser, void *ptr) +{ + FREE(ptr); +} + +void XMLCALL +XML_DefaultCurrent(XML_Parser parser) +{ + if (defaultHandler) { + if (openInternalEntities) + reportDefault(parser, + internalEncoding, + openInternalEntities->internalEventPtr, + openInternalEntities->internalEventEndPtr); + else + reportDefault(parser, encoding, eventPtr, eventEndPtr); + } +} + +const XML_LChar * XMLCALL +XML_ErrorString(enum XML_Error code) +{ + static const XML_LChar* const message[] = { + 0, + XML_L("out of memory"), + XML_L("syntax error"), + XML_L("no element found"), + XML_L("not well-formed (invalid token)"), + XML_L("unclosed token"), + XML_L("partial character"), + XML_L("mismatched tag"), + XML_L("duplicate attribute"), + XML_L("junk after document element"), + XML_L("illegal parameter entity reference"), + XML_L("undefined entity"), + XML_L("recursive entity reference"), + XML_L("asynchronous entity"), + XML_L("reference to invalid character number"), + XML_L("reference to binary entity"), + XML_L("reference to external entity in attribute"), + XML_L("XML or text declaration not at start of entity"), + XML_L("unknown encoding"), + XML_L("encoding specified in XML declaration is incorrect"), + XML_L("unclosed CDATA section"), + XML_L("error in processing external entity reference"), + XML_L("document is not standalone"), + XML_L("unexpected parser state - please send a bug report"), + XML_L("entity declared in parameter entity"), + XML_L("requested feature requires XML_DTD support in Expat"), + XML_L("cannot change setting once parsing has begun"), + XML_L("unbound prefix"), + XML_L("must not undeclare prefix"), + XML_L("incomplete markup in parameter entity"), + XML_L("XML declaration not well-formed"), + XML_L("text declaration not well-formed"), + XML_L("illegal character(s) in public id"), + XML_L("parser suspended"), + XML_L("parser not suspended"), + XML_L("parsing aborted"), + XML_L("parsing finished"), + XML_L("cannot suspend in external parameter entity"), + XML_L("reserved prefix (xml) must not be undeclared or bound to another namespace name"), + XML_L("reserved prefix (xmlns) must not be declared or undeclared"), + XML_L("prefix must not be bound to one of the reserved namespace names") + }; + if (code > 0 && code < sizeof(message)/sizeof(message[0])) + return message[code]; + return NULL; +} + +const XML_LChar * XMLCALL +XML_ExpatVersion(void) { + + /* V1 is used to string-ize the version number. However, it would + string-ize the actual version macro *names* unless we get them + substituted before being passed to V1. CPP is defined to expand + a macro, then rescan for more expansions. Thus, we use V2 to expand + the version macros, then CPP will expand the resulting V1() macro + with the correct numerals. */ + /* ### I'm assuming cpp is portable in this respect... */ + +#define V1(a,b,c) XML_L(#a)XML_L(".")XML_L(#b)XML_L(".")XML_L(#c) +#define V2(a,b,c) XML_L("expat_")V1(a,b,c) + + return V2(XML_MAJOR_VERSION, XML_MINOR_VERSION, XML_MICRO_VERSION); + +#undef V1 +#undef V2 +} + +XML_Expat_Version XMLCALL +XML_ExpatVersionInfo(void) +{ + XML_Expat_Version version; + + version.major = XML_MAJOR_VERSION; + version.minor = XML_MINOR_VERSION; + version.micro = XML_MICRO_VERSION; + + return version; +} + +const XML_Feature * XMLCALL +XML_GetFeatureList(void) +{ + static const XML_Feature features[] = { + {XML_FEATURE_SIZEOF_XML_CHAR, XML_L("sizeof(XML_Char)"), + sizeof(XML_Char)}, + {XML_FEATURE_SIZEOF_XML_LCHAR, XML_L("sizeof(XML_LChar)"), + sizeof(XML_LChar)}, +#ifdef XML_UNICODE + {XML_FEATURE_UNICODE, XML_L("XML_UNICODE"), 0}, +#endif +#ifdef XML_UNICODE_WCHAR_T + {XML_FEATURE_UNICODE_WCHAR_T, XML_L("XML_UNICODE_WCHAR_T"), 0}, +#endif +#ifdef XML_DTD + {XML_FEATURE_DTD, XML_L("XML_DTD"), 0}, +#endif +#ifdef XML_CONTEXT_BYTES + {XML_FEATURE_CONTEXT_BYTES, XML_L("XML_CONTEXT_BYTES"), + XML_CONTEXT_BYTES}, +#endif +#ifdef XML_MIN_SIZE + {XML_FEATURE_MIN_SIZE, XML_L("XML_MIN_SIZE"), 0}, +#endif +#ifdef XML_NS + {XML_FEATURE_NS, XML_L("XML_NS"), 0}, +#endif +#ifdef XML_LARGE_SIZE + {XML_FEATURE_LARGE_SIZE, XML_L("XML_LARGE_SIZE"), 0}, +#endif +#ifdef XML_ATTR_INFO + {XML_FEATURE_ATTR_INFO, XML_L("XML_ATTR_INFO"), 0}, +#endif + {XML_FEATURE_END, NULL, 0} + }; + + return features; +} + +/* Initially tag->rawName always points into the parse buffer; + for those TAG instances opened while the current parse buffer was + processed, and not yet closed, we need to store tag->rawName in a more + permanent location, since the parse buffer is about to be discarded. +*/ +static XML_Bool +storeRawNames(XML_Parser parser) +{ + TAG *tag = tagStack; + while (tag) { + int bufSize; + int nameLen = sizeof(XML_Char) * (tag->name.strLen + 1); + char *rawNameBuf = tag->buf + nameLen; + /* Stop if already stored. Since tagStack is a stack, we can stop + at the first entry that has already been copied; everything + below it in the stack is already been accounted for in a + previous call to this function. + */ + if (tag->rawName == rawNameBuf) + break; + /* For re-use purposes we need to ensure that the + size of tag->buf is a multiple of sizeof(XML_Char). + */ + bufSize = nameLen + ROUND_UP(tag->rawNameLength, sizeof(XML_Char)); + if (bufSize > tag->bufEnd - tag->buf) { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_FALSE; + /* if tag->name.str points to tag->buf (only when namespace + processing is off) then we have to update it + */ + if (tag->name.str == (XML_Char *)tag->buf) + tag->name.str = (XML_Char *)temp; + /* if tag->name.localPart is set (when namespace processing is on) + then update it as well, since it will always point into tag->buf + */ + if (tag->name.localPart) + tag->name.localPart = (XML_Char *)temp + (tag->name.localPart - + (XML_Char *)tag->buf); + tag->buf = temp; + tag->bufEnd = temp + bufSize; + rawNameBuf = temp + nameLen; + } + memcpy(rawNameBuf, tag->rawName, tag->rawNameLength); + tag->rawName = rawNameBuf; + tag = tag->parent; + } + return XML_TRUE; +} + +static enum XML_Error PTRCALL +contentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doContent(parser, 0, encoding, start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result == XML_ERROR_NONE) { + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = externalEntityInitProcessor2; + return externalEntityInitProcessor2(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor2(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(encoding, start, end, &next); + switch (tok) { + case XML_TOK_BOM: + /* If we are at the end of the buffer, this would cause the next stage, + i.e. externalEntityInitProcessor3, to pass control directly to + doContent (by detecting XML_TOK_NONE) without processing any xml text + declaration - causing the error XML_ERROR_MISPLACED_XML_PI in doContent. + */ + if (next == end && !ps_finalBuffer) { + *endPtr = next; + return XML_ERROR_NONE; + } + start = next; + break; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + eventPtr = start; + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityInitProcessor3; + return externalEntityInitProcessor3(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityInitProcessor3(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + int tok; + const char *next = start; /* XmlContentTok doesn't always set the last arg */ + eventPtr = start; + tok = XmlContentTok(encoding, start, end, &next); + eventEndPtr = next; + + switch (tok) { + case XML_TOK_XML_DECL: + { + enum XML_Error result; + result = processXmlDecl(parser, 1, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (ps_parsing) { + case XML_SUSPENDED: + *endPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + start = next; + } + } + break; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *endPtr = start; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + } + processor = externalEntityContentProcessor; + tagLevel = 1; + return externalEntityContentProcessor(parser, start, end, endPtr); +} + +static enum XML_Error PTRCALL +externalEntityContentProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doContent(parser, 1, encoding, start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result == XML_ERROR_NONE) { + if (!storeRawNames(parser)) + return XML_ERROR_NO_MEMORY; + } + return result; +} + +static enum XML_Error +doContent(XML_Parser parser, + int startTagLevel, + const ENCODING *enc, + const char *s, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + /* save one level of indirection */ + DTD * const dtd = _dtd; + + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + + for (;;) { + const char *next = s; /* XmlContentTok doesn't always set the last arg */ + int tok = XmlContentTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_TRAILING_CR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + *eventEndPP = end; + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? + */ + if (startTagLevel == 0) + return XML_ERROR_NO_ELEMENTS; + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (startTagLevel > 0) { + if (tagLevel != startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_NO_ELEMENTS; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (characterDataHandler) + characterDataHandler(handlerArg, &ch, 1); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); + poolDiscard(&dtd->pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity or default handler. + */ + if (!dtd->hasParamEntityRefs || dtd->standalone) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->notation) + return XML_ERROR_BINARY_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + if (!defaultExpandInternalEntities) { + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, entity->name, 0); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + result = processInternalEntity(parser, entity, XML_FALSE); + if (result != XML_ERROR_NONE) + return result; + } + else if (externalEntityRefHandler) { + const XML_Char *context; + entity->open = XML_TRUE; + context = getContext(parser); + entity->open = XML_FALSE; + if (!context) + return XML_ERROR_NO_MEMORY; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + context, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + poolDiscard(&tempPool); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + case XML_TOK_START_TAG_NO_ATTS: + /* fall through */ + case XML_TOK_START_TAG_WITH_ATTS: + { + TAG *tag; + enum XML_Error result; + XML_Char *toPtr; + if (freeTagList) { + tag = freeTagList; + freeTagList = freeTagList->parent; + } + else { + tag = (TAG *)MALLOC(sizeof(TAG)); + if (!tag) + return XML_ERROR_NO_MEMORY; + tag->buf = (char *)MALLOC(INIT_TAG_BUF_SIZE); + if (!tag->buf) { + FREE(tag); + return XML_ERROR_NO_MEMORY; + } + tag->bufEnd = tag->buf + INIT_TAG_BUF_SIZE; + } + tag->bindings = NULL; + tag->parent = tagStack; + tagStack = tag; + tag->name.localPart = NULL; + tag->name.prefix = NULL; + tag->rawName = s + enc->minBytesPerChar; + tag->rawNameLength = XmlNameLength(enc, tag->rawName); + ++tagLevel; + { + const char *rawNameEnd = tag->rawName + tag->rawNameLength; + const char *fromPtr = tag->rawName; + toPtr = (XML_Char *)tag->buf; + for (;;) { + int bufSize; + int convLen; + XmlConvert(enc, + &fromPtr, rawNameEnd, + (ICHAR **)&toPtr, (ICHAR *)tag->bufEnd - 1); + convLen = (int)(toPtr - (XML_Char *)tag->buf); + if (fromPtr == rawNameEnd) { + tag->name.strLen = convLen; + break; + } + bufSize = (int)(tag->bufEnd - tag->buf) << 1; + { + char *temp = (char *)REALLOC(tag->buf, bufSize); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + tag->buf = temp; + tag->bufEnd = temp + bufSize; + toPtr = (XML_Char *)temp + convLen; + } + } + } + tag->name.str = (XML_Char *)tag->buf; + *toPtr = XML_T('\0'); + result = storeAtts(parser, enc, s, &(tag->name), &(tag->bindings)); + if (result) + return result; + if (startElementHandler) + startElementHandler(handlerArg, tag->name.str, + (const XML_Char **)atts); + else if (defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + break; + } + case XML_TOK_EMPTY_ELEMENT_NO_ATTS: + /* fall through */ + case XML_TOK_EMPTY_ELEMENT_WITH_ATTS: + { + const char *rawName = s + enc->minBytesPerChar; + enum XML_Error result; + BINDING *bindings = NULL; + XML_Bool noElmHandlers = XML_TRUE; + TAG_NAME name; + name.str = poolStoreString(&tempPool, enc, rawName, + rawName + XmlNameLength(enc, rawName)); + if (!name.str) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + result = storeAtts(parser, enc, s, &name, &bindings); + if (result) + return result; + poolFinish(&tempPool); + if (startElementHandler) { + startElementHandler(handlerArg, name.str, (const XML_Char **)atts); + noElmHandlers = XML_FALSE; + } + if (endElementHandler) { + if (startElementHandler) + *eventPP = *eventEndPP; + endElementHandler(handlerArg, name.str); + noElmHandlers = XML_FALSE; + } + if (noElmHandlers && defaultHandler) + reportDefault(parser, enc, s, next); + poolClear(&tempPool); + while (bindings) { + BINDING *b = bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + bindings = bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + break; + case XML_TOK_END_TAG: + if (tagLevel == startTagLevel) + return XML_ERROR_ASYNC_ENTITY; + else { + int len; + const char *rawName; + TAG *tag = tagStack; + tagStack = tag->parent; + tag->parent = freeTagList; + freeTagList = tag; + rawName = s + enc->minBytesPerChar*2; + len = XmlNameLength(enc, rawName); + if (len != tag->rawNameLength + || memcmp(tag->rawName, rawName, len) != 0) { + *eventPP = rawName; + return XML_ERROR_TAG_MISMATCH; + } + --tagLevel; + if (endElementHandler) { + const XML_Char *localPart; + const XML_Char *prefix; + XML_Char *uri; + localPart = tag->name.localPart; + if (ns && localPart) { + /* localPart and prefix may have been overwritten in + tag->name.str, since this points to the binding->uri + buffer which gets re-used; so we have to add them again + */ + uri = (XML_Char *)tag->name.str + tag->name.uriLen; + /* don't need to check for space - already done in storeAtts() */ + while (*localPart) *uri++ = *localPart++; + prefix = (XML_Char *)tag->name.prefix; + if (ns_triplets && prefix) { + *uri++ = namespaceSeparator; + while (*prefix) *uri++ = *prefix++; + } + *uri = XML_T('\0'); + } + endElementHandler(handlerArg, tag->name.str); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + while (tag->bindings) { + BINDING *b = tag->bindings; + if (endNamespaceDeclHandler) + endNamespaceDeclHandler(handlerArg, b->prefix->name); + tag->bindings = tag->bindings->nextTagBinding; + b->nextTagBinding = freeBindingList; + freeBindingList = b; + b->prefix->binding = b->prevPrefixBinding; + } + if (tagLevel == 0) + return epilogProcessor(parser, next, end, nextPtr); + } + break; + case XML_TOK_CHAR_REF: + { + int n = XmlCharRefNumber(enc, s); + if (n < 0) + return XML_ERROR_BAD_CHAR_REF; + if (characterDataHandler) { + XML_Char buf[XML_ENCODE_MAX]; + characterDataHandler(handlerArg, buf, XmlEncode(n, (ICHAR *)buf)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_CDATA_SECT_OPEN: + { + enum XML_Error result; + if (startCdataSectionHandler) + startCdataSectionHandler(handlerArg); +#if 0 + /* Suppose you doing a transformation on a document that involves + changing only the character data. You set up a defaultHandler + and a characterDataHandler. The defaultHandler simply copies + characters through. The characterDataHandler does the + transformation and writes the characters out escaping them as + necessary. This case will fail to work if we leave out the + following two lines (because & and < inside CDATA sections will + be incorrectly escaped). + + However, now we have a start/endCdataSectionHandler, so it seems + easier to let the user deal with this. + */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + result = doCdataSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (!next) { + processor = cdataSectionProcessor; + return result; + } + } + break; + case XML_TOK_TRAILING_RSQB: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + if (characterDataHandler) { + if (MUST_CONVERT(enc, s)) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + characterDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + } + else + characterDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)end - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, end); + /* We are at the end of the final buffer, should we check for + XML_SUSPENDED, XML_FINISHED? + */ + if (startTagLevel == 0) { + *eventPP = end; + return XML_ERROR_NO_ELEMENTS; + } + if (tagLevel != startTagLevel) { + *eventPP = end; + return XML_ERROR_ASYNC_ENTITY; + } + *nextPtr = end; + return XML_ERROR_NONE; + case XML_TOK_DATA_CHARS: + { + XML_CharacterDataHandler charDataHandler = characterDataHandler; + if (charDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + charDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + if (s == next) + break; + *eventPP = s; + } + } + else + charDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)next - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + break; + default: + if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + } + *eventPP = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } + /* not reached */ +} + +/* Precondition: all arguments must be non-NULL; + Purpose: + - normalize attributes + - check attributes for well-formedness + - generate namespace aware attribute names (URI, prefix) + - build list of attributes for startElementHandler + - default attributes + - process namespace declarations (check and report them) + - generate namespace aware element name (URI, prefix) +*/ +static enum XML_Error +storeAtts(XML_Parser parser, const ENCODING *enc, + const char *attStr, TAG_NAME *tagNamePtr, + BINDING **bindingsPtr) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ELEMENT_TYPE *elementType; + int nDefaultAtts; + const XML_Char **appAtts; /* the attribute list for the application */ + int attIndex = 0; + int prefixLen; + int i; + int n; + XML_Char *uri; + int nPrefixes = 0; + BINDING *binding; + const XML_Char *localPart; + + /* lookup the element type name */ + elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, tagNamePtr->str,0); + if (!elementType) { + const XML_Char *name = poolCopyString(&dtd->pool, tagNamePtr->str); + if (!name) + return XML_ERROR_NO_MEMORY; + elementType = (ELEMENT_TYPE *)lookup(parser, &dtd->elementTypes, name, + sizeof(ELEMENT_TYPE)); + if (!elementType) + return XML_ERROR_NO_MEMORY; + if (ns && !setElementTypePrefix(parser, elementType)) + return XML_ERROR_NO_MEMORY; + } + nDefaultAtts = elementType->nDefaultAtts; + + /* get the attributes from the tokenizer */ + n = XmlGetAttributes(enc, attStr, attsSize, atts); + if (n + nDefaultAtts > attsSize) { + int oldAttsSize = attsSize; + ATTRIBUTE *temp; +#ifdef XML_ATTR_INFO + XML_AttrInfo *temp2; +#endif + attsSize = n + nDefaultAtts + INIT_ATTS_SIZE; + temp = (ATTRIBUTE *)REALLOC((void *)atts, attsSize * sizeof(ATTRIBUTE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + atts = temp; +#ifdef XML_ATTR_INFO + temp2 = (XML_AttrInfo *)REALLOC((void *)attInfo, attsSize * sizeof(XML_AttrInfo)); + if (temp2 == NULL) + return XML_ERROR_NO_MEMORY; + attInfo = temp2; +#endif + if (n > oldAttsSize) + XmlGetAttributes(enc, attStr, n, atts); + } + + appAtts = (const XML_Char **)atts; + for (i = 0; i < n; i++) { + ATTRIBUTE *currAtt = &atts[i]; +#ifdef XML_ATTR_INFO + XML_AttrInfo *currAttInfo = &attInfo[i]; +#endif + /* add the name and value to the attribute list */ + ATTRIBUTE_ID *attId = getAttributeId(parser, enc, currAtt->name, + currAtt->name + + XmlNameLength(enc, currAtt->name)); + if (!attId) + return XML_ERROR_NO_MEMORY; +#ifdef XML_ATTR_INFO + currAttInfo->nameStart = parseEndByteIndex - (parseEndPtr - currAtt->name); + currAttInfo->nameEnd = currAttInfo->nameStart + + XmlNameLength(enc, currAtt->name); + currAttInfo->valueStart = parseEndByteIndex - + (parseEndPtr - currAtt->valuePtr); + currAttInfo->valueEnd = parseEndByteIndex - (parseEndPtr - currAtt->valueEnd); +#endif + /* Detect duplicate attributes by their QNames. This does not work when + namespace processing is turned on and different prefixes for the same + namespace are used. For this case we have a check further down. + */ + if ((attId->name)[-1]) { + if (enc == encoding) + eventPtr = atts[i].name; + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + (attId->name)[-1] = 1; + appAtts[attIndex++] = attId->name; + if (!atts[i].normalized) { + enum XML_Error result; + XML_Bool isCdata = XML_TRUE; + + /* figure out whether declared as other than CDATA */ + if (attId->maybeTokenized) { + int j; + for (j = 0; j < nDefaultAtts; j++) { + if (attId == elementType->defaultAtts[j].id) { + isCdata = elementType->defaultAtts[j].isCdata; + break; + } + } + } + + /* normalize the attribute value */ + result = storeAttributeValue(parser, enc, isCdata, + atts[i].valuePtr, atts[i].valueEnd, + &tempPool); + if (result) + return result; + appAtts[attIndex] = poolStart(&tempPool); + poolFinish(&tempPool); + } + else { + /* the value did not need normalizing */ + appAtts[attIndex] = poolStoreString(&tempPool, enc, atts[i].valuePtr, + atts[i].valueEnd); + if (appAtts[attIndex] == 0) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + } + /* handle prefixed attribute names */ + if (attId->prefix) { + if (attId->xmlns) { + /* deal with namespace declarations here */ + enum XML_Error result = addBinding(parser, attId->prefix, attId, + appAtts[attIndex], bindingsPtr); + if (result) + return result; + --attIndex; + } + else { + /* deal with other prefixed names later */ + attIndex++; + nPrefixes++; + (attId->name)[-1] = 2; + } + } + else + attIndex++; + } + + /* set-up for XML_GetSpecifiedAttributeCount and XML_GetIdAttributeIndex */ + nSpecifiedAtts = attIndex; + if (elementType->idAtt && (elementType->idAtt->name)[-1]) { + for (i = 0; i < attIndex; i += 2) + if (appAtts[i] == elementType->idAtt->name) { + idAttIndex = i; + break; + } + } + else + idAttIndex = -1; + + /* do attribute defaulting */ + for (i = 0; i < nDefaultAtts; i++) { + const DEFAULT_ATTRIBUTE *da = elementType->defaultAtts + i; + if (!(da->id->name)[-1] && da->value) { + if (da->id->prefix) { + if (da->id->xmlns) { + enum XML_Error result = addBinding(parser, da->id->prefix, da->id, + da->value, bindingsPtr); + if (result) + return result; + } + else { + (da->id->name)[-1] = 2; + nPrefixes++; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + else { + (da->id->name)[-1] = 1; + appAtts[attIndex++] = da->id->name; + appAtts[attIndex++] = da->value; + } + } + } + appAtts[attIndex] = 0; + + /* expand prefixed attribute names, check for duplicates, + and clear flags that say whether attributes were specified */ + i = 0; + if (nPrefixes) { + int j; /* hash table index */ + unsigned long version = nsAttsVersion; + int nsAttsSize = (int)1 << nsAttsPower; + /* size of hash table must be at least 2 * (# of prefixed attributes) */ + if ((nPrefixes << 1) >> nsAttsPower) { /* true for nsAttsPower = 0 */ + NS_ATT *temp; + /* hash table size must also be a power of 2 and >= 8 */ + while (nPrefixes >> nsAttsPower++); + if (nsAttsPower < 3) + nsAttsPower = 3; + nsAttsSize = (int)1 << nsAttsPower; + temp = (NS_ATT *)REALLOC(nsAtts, nsAttsSize * sizeof(NS_ATT)); + if (!temp) + return XML_ERROR_NO_MEMORY; + nsAtts = temp; + version = 0; /* force re-initialization of nsAtts hash table */ + } + /* using a version flag saves us from initializing nsAtts every time */ + if (!version) { /* initialize version flags when version wraps around */ + version = INIT_ATTS_VERSION; + for (j = nsAttsSize; j != 0; ) + nsAtts[--j].version = version; + } + nsAttsVersion = --version; + + /* expand prefixed names and check for duplicates */ + for (; i < attIndex; i += 2) { + const XML_Char *s = appAtts[i]; + if (s[-1] == 2) { /* prefixed */ + ATTRIBUTE_ID *id; + const BINDING *b; + unsigned long uriHash = hash_secret_salt; + ((XML_Char *)s)[-1] = 0; /* clear flag */ + id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, s, 0); + b = id->prefix->binding; + if (!b) + return XML_ERROR_UNBOUND_PREFIX; + + /* as we expand the name we also calculate its hash value */ + for (j = 0; j < b->uriLen; j++) { + const XML_Char c = b->uri[j]; + if (!poolAppendChar(&tempPool, c)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } + while (*s++ != XML_T(ASCII_COLON)) + ; + do { /* copies null terminator */ + const XML_Char c = *s; + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + uriHash = CHAR_HASH(uriHash, c); + } while (*s++); + + { /* Check hash table for duplicate of expanded name (uriName). + Derived from code in lookup(parser, HASH_TABLE *table, ...). + */ + unsigned char step = 0; + unsigned long mask = nsAttsSize - 1; + j = uriHash & mask; /* index into hash table */ + while (nsAtts[j].version == version) { + /* for speed we compare stored hash values first */ + if (uriHash == nsAtts[j].hash) { + const XML_Char *s1 = poolStart(&tempPool); + const XML_Char *s2 = nsAtts[j].uriName; + /* s1 is null terminated, but not s2 */ + for (; *s1 == *s2 && *s1 != 0; s1++, s2++); + if (*s1 == 0) + return XML_ERROR_DUPLICATE_ATTRIBUTE; + } + if (!step) + step = PROBE_STEP(uriHash, mask, nsAttsPower); + j < step ? (j += nsAttsSize - step) : (j -= step); + } + } + + if (ns_triplets) { /* append namespace separator and prefix */ + tempPool.ptr[-1] = namespaceSeparator; + s = b->prefix->name; + do { + if (!poolAppendChar(&tempPool, *s)) + return XML_ERROR_NO_MEMORY; + } while (*s++); + } + + /* store expanded name in attribute list */ + s = poolStart(&tempPool); + poolFinish(&tempPool); + appAtts[i] = s; + + /* fill empty slot with new version, uriName and hash value */ + nsAtts[j].version = version; + nsAtts[j].hash = uriHash; + nsAtts[j].uriName = s; + + if (!--nPrefixes) { + i += 2; + break; + } + } + else /* not prefixed */ + ((XML_Char *)s)[-1] = 0; /* clear flag */ + } + } + /* clear flags for the remaining attributes */ + for (; i < attIndex; i += 2) + ((XML_Char *)(appAtts[i]))[-1] = 0; + for (binding = *bindingsPtr; binding; binding = binding->nextTagBinding) + binding->attId->name[-1] = 0; + + if (!ns) + return XML_ERROR_NONE; + + /* expand the element type name */ + if (elementType->prefix) { + binding = elementType->prefix->binding; + if (!binding) + return XML_ERROR_UNBOUND_PREFIX; + localPart = tagNamePtr->str; + while (*localPart++ != XML_T(ASCII_COLON)) + ; + } + else if (dtd->defaultPrefix.binding) { + binding = dtd->defaultPrefix.binding; + localPart = tagNamePtr->str; + } + else + return XML_ERROR_NONE; + prefixLen = 0; + if (ns_triplets && binding->prefix->name) { + for (; binding->prefix->name[prefixLen++];) + ; /* prefixLen includes null terminator */ + } + tagNamePtr->localPart = localPart; + tagNamePtr->uriLen = binding->uriLen; + tagNamePtr->prefix = binding->prefix->name; + tagNamePtr->prefixLen = prefixLen; + for (i = 0; localPart[i++];) + ; /* i includes null terminator */ + n = i + binding->uriLen + prefixLen; + if (n > binding->uriAlloc) { + TAG *p; + uri = (XML_Char *)MALLOC((n + EXPAND_SPARE) * sizeof(XML_Char)); + if (!uri) + return XML_ERROR_NO_MEMORY; + binding->uriAlloc = n + EXPAND_SPARE; + memcpy(uri, binding->uri, binding->uriLen * sizeof(XML_Char)); + for (p = tagStack; p; p = p->parent) + if (p->name.str == binding->uri) + p->name.str = uri; + FREE(binding->uri); + binding->uri = uri; + } + /* if namespaceSeparator != '\0' then uri includes it already */ + uri = binding->uri + binding->uriLen; + memcpy(uri, localPart, i * sizeof(XML_Char)); + /* we always have a namespace separator between localPart and prefix */ + if (prefixLen) { + uri += i - 1; + *uri = namespaceSeparator; /* replace null terminator */ + memcpy(uri + 1, binding->prefix->name, prefixLen * sizeof(XML_Char)); + } + tagNamePtr->str = binding->uri; + return XML_ERROR_NONE; +} + +/* addBinding() overwrites the value of prefix->binding without checking. + Therefore one must keep track of the old value outside of addBinding(). +*/ +static enum XML_Error +addBinding(XML_Parser parser, PREFIX *prefix, const ATTRIBUTE_ID *attId, + const XML_Char *uri, BINDING **bindingsPtr) +{ + static const XML_Char xmlNamespace[] = { + ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, + ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, + ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_X, ASCII_M, ASCII_L, + ASCII_SLASH, ASCII_1, ASCII_9, ASCII_9, ASCII_8, ASCII_SLASH, + ASCII_n, ASCII_a, ASCII_m, ASCII_e, ASCII_s, ASCII_p, ASCII_a, ASCII_c, + ASCII_e, '\0' + }; + static const int xmlLen = + (int)sizeof(xmlNamespace)/sizeof(XML_Char) - 1; + static const XML_Char xmlnsNamespace[] = { + ASCII_h, ASCII_t, ASCII_t, ASCII_p, ASCII_COLON, ASCII_SLASH, ASCII_SLASH, + ASCII_w, ASCII_w, ASCII_w, ASCII_PERIOD, ASCII_w, ASCII_3, ASCII_PERIOD, + ASCII_o, ASCII_r, ASCII_g, ASCII_SLASH, ASCII_2, ASCII_0, ASCII_0, + ASCII_0, ASCII_SLASH, ASCII_x, ASCII_m, ASCII_l, ASCII_n, ASCII_s, + ASCII_SLASH, '\0' + }; + static const int xmlnsLen = + (int)sizeof(xmlnsNamespace)/sizeof(XML_Char) - 1; + + XML_Bool mustBeXML = XML_FALSE; + XML_Bool isXML = XML_TRUE; + XML_Bool isXMLNS = XML_TRUE; + + BINDING *b; + int len; + + /* empty URI is only valid for default namespace per XML NS 1.0 (not 1.1) */ + if (*uri == XML_T('\0') && prefix->name) + return XML_ERROR_UNDECLARING_PREFIX; + + if (prefix->name + && prefix->name[0] == XML_T(ASCII_x) + && prefix->name[1] == XML_T(ASCII_m) + && prefix->name[2] == XML_T(ASCII_l)) { + + /* Not allowed to bind xmlns */ + if (prefix->name[3] == XML_T(ASCII_n) + && prefix->name[4] == XML_T(ASCII_s) + && prefix->name[5] == XML_T('\0')) + return XML_ERROR_RESERVED_PREFIX_XMLNS; + + if (prefix->name[3] == XML_T('\0')) + mustBeXML = XML_TRUE; + } + + for (len = 0; uri[len]; len++) { + if (isXML && (len > xmlLen || uri[len] != xmlNamespace[len])) + isXML = XML_FALSE; + + if (!mustBeXML && isXMLNS + && (len > xmlnsLen || uri[len] != xmlnsNamespace[len])) + isXMLNS = XML_FALSE; + } + isXML = isXML && len == xmlLen; + isXMLNS = isXMLNS && len == xmlnsLen; + + if (mustBeXML != isXML) + return mustBeXML ? XML_ERROR_RESERVED_PREFIX_XML + : XML_ERROR_RESERVED_NAMESPACE_URI; + + if (isXMLNS) + return XML_ERROR_RESERVED_NAMESPACE_URI; + + if (namespaceSeparator) + len++; + if (freeBindingList) { + b = freeBindingList; + if (len > b->uriAlloc) { + XML_Char *temp = (XML_Char *)REALLOC(b->uri, + sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + b->uri = temp; + b->uriAlloc = len + EXPAND_SPARE; + } + freeBindingList = b->nextTagBinding; + } + else { + b = (BINDING *)MALLOC(sizeof(BINDING)); + if (!b) + return XML_ERROR_NO_MEMORY; + b->uri = (XML_Char *)MALLOC(sizeof(XML_Char) * (len + EXPAND_SPARE)); + if (!b->uri) { + FREE(b); + return XML_ERROR_NO_MEMORY; + } + b->uriAlloc = len + EXPAND_SPARE; + } + b->uriLen = len; + memcpy(b->uri, uri, len * sizeof(XML_Char)); + if (namespaceSeparator) + b->uri[len - 1] = namespaceSeparator; + b->prefix = prefix; + b->attId = attId; + b->prevPrefixBinding = prefix->binding; + /* NULL binding when default namespace undeclared */ + if (*uri == XML_T('\0') && prefix == &_dtd->defaultPrefix) + prefix->binding = NULL; + else + prefix->binding = b; + b->nextTagBinding = *bindingsPtr; + *bindingsPtr = b; + /* if attId == NULL then we are not starting a namespace scope */ + if (attId && startNamespaceDeclHandler) + startNamespaceDeclHandler(handlerArg, prefix->name, + prefix->binding ? uri : 0); + return XML_ERROR_NONE; +} + +/* The idea here is to avoid using stack for each CDATA section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +cdataSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doCdataSection(parser, encoding, &start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result != XML_ERROR_NONE) + return result; + if (start) { + if (parentParser) { /* we are parsing an external entity */ + processor = externalEntityContentProcessor; + return externalEntityContentProcessor(parser, start, end, endPtr); + } + else { + processor = contentProcessor; + return contentProcessor(parser, start, end, endPtr); + } + } + return result; +} + +/* startPtr gets set to non-null if the section is closed, and to null if + the section is not yet closed. +*/ +static enum XML_Error +doCdataSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + + for (;;) { + const char *next; + int tok = XmlCdataSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_CDATA_SECT_CLOSE: + if (endCdataSectionHandler) + endCdataSectionHandler(handlerArg); +#if 0 + /* see comment under XML_TOK_CDATA_SECT_OPEN */ + else if (characterDataHandler) + characterDataHandler(handlerArg, dataBuf, 0); +#endif + else if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + *nextPtr = next; + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + else + return XML_ERROR_NONE; + case XML_TOK_DATA_NEWLINE: + if (characterDataHandler) { + XML_Char c = 0xA; + characterDataHandler(handlerArg, &c, 1); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + break; + case XML_TOK_DATA_CHARS: + { + XML_CharacterDataHandler charDataHandler = characterDataHandler; + if (charDataHandler) { + if (MUST_CONVERT(enc, s)) { + for (;;) { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, next, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = next; + charDataHandler(handlerArg, dataBuf, + (int)(dataPtr - (ICHAR *)dataBuf)); + if (s == next) + break; + *eventPP = s; + } + } + else + charDataHandler(handlerArg, + (XML_Char *)s, + (int)((XML_Char *)next - (XML_Char *)s)); + } + else if (defaultHandler) + reportDefault(parser, enc, s, next); + } + break; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_CDATA_SECTION; + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + + *eventPP = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } + /* not reached */ +} + +#ifdef XML_DTD + +/* The idea here is to avoid using stack for each IGNORE section when + the whole file is parsed with one call. +*/ +static enum XML_Error PTRCALL +ignoreSectionProcessor(XML_Parser parser, + const char *start, + const char *end, + const char **endPtr) +{ + enum XML_Error result = doIgnoreSection(parser, encoding, &start, end, + endPtr, (XML_Bool)!ps_finalBuffer); + if (result != XML_ERROR_NONE) + return result; + if (start) { + processor = prologProcessor; + return prologProcessor(parser, start, end, endPtr); + } + return result; +} + +/* startPtr gets set to non-null is the section is closed, and to null + if the section is not yet closed. +*/ +static enum XML_Error +doIgnoreSection(XML_Parser parser, + const ENCODING *enc, + const char **startPtr, + const char *end, + const char **nextPtr, + XML_Bool haveMore) +{ + const char *next; + int tok; + const char *s = *startPtr; + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + *eventPP = s; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + *eventPP = s; + *startPtr = NULL; + tok = XmlIgnoreSectionTok(enc, s, end, &next); + *eventEndPP = next; + switch (tok) { + case XML_TOK_IGNORE_SECT: + if (defaultHandler) + reportDefault(parser, enc, s, next); + *startPtr = next; + *nextPtr = next; + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + else + return XML_ERROR_NONE; + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_PARTIAL: + case XML_TOK_NONE: + if (haveMore) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_SYNTAX; /* XML_ERROR_UNCLOSED_IGNORE_SECTION */ + default: + *eventPP = next; + return XML_ERROR_UNEXPECTED_STATE; + } + /* not reached */ +} + +#endif /* XML_DTD */ + +static enum XML_Error +initializeEncoding(XML_Parser parser) +{ + const char *s; +#ifdef XML_UNICODE + char encodingBuf[128]; + if (!protocolEncodingName) + s = NULL; + else { + int i; + for (i = 0; protocolEncodingName[i]; i++) { + if (i == sizeof(encodingBuf) - 1 + || (protocolEncodingName[i] & ~0x7f) != 0) { + encodingBuf[0] = '\0'; + break; + } + encodingBuf[i] = (char)protocolEncodingName[i]; + } + encodingBuf[i] = '\0'; + s = encodingBuf; + } +#else + s = protocolEncodingName; +#endif + if ((ns ? XmlInitEncodingNS : XmlInitEncoding)(&initEncoding, &encoding, s)) + return XML_ERROR_NONE; + return handleUnknownEncoding(parser, protocolEncodingName); +} + +static enum XML_Error +processXmlDecl(XML_Parser parser, int isGeneralTextEntity, + const char *s, const char *next) +{ + const char *encodingName = NULL; + const XML_Char *storedEncName = NULL; + const ENCODING *newEncoding = NULL; + const char *version = NULL; + const char *versionend; + const XML_Char *storedversion = NULL; + int standalone = -1; + if (!(ns + ? XmlParseXmlDeclNS + : XmlParseXmlDecl)(isGeneralTextEntity, + encoding, + s, + next, + &eventPtr, + &version, + &versionend, + &encodingName, + &newEncoding, + &standalone)) { + if (isGeneralTextEntity) + return XML_ERROR_TEXT_DECL; + else + return XML_ERROR_XML_DECL; + } + if (!isGeneralTextEntity && standalone == 1) { + _dtd->standalone = XML_TRUE; +#ifdef XML_DTD + if (paramEntityParsing == XML_PARAM_ENTITY_PARSING_UNLESS_STANDALONE) + paramEntityParsing = XML_PARAM_ENTITY_PARSING_NEVER; +#endif /* XML_DTD */ + } + if (xmlDeclHandler) { + if (encodingName != NULL) { + storedEncName = poolStoreString(&temp2Pool, + encoding, + encodingName, + encodingName + + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + poolFinish(&temp2Pool); + } + if (version) { + storedversion = poolStoreString(&temp2Pool, + encoding, + version, + versionend - encoding->minBytesPerChar); + if (!storedversion) + return XML_ERROR_NO_MEMORY; + } + xmlDeclHandler(handlerArg, storedversion, storedEncName, standalone); + } + else if (defaultHandler) + reportDefault(parser, encoding, s, next); + if (protocolEncodingName == NULL) { + if (newEncoding) { + if (newEncoding->minBytesPerChar != encoding->minBytesPerChar) { + eventPtr = encodingName; + return XML_ERROR_INCORRECT_ENCODING; + } + encoding = newEncoding; + } + else if (encodingName) { + enum XML_Error result; + if (!storedEncName) { + storedEncName = poolStoreString( + &temp2Pool, encoding, encodingName, + encodingName + XmlNameLength(encoding, encodingName)); + if (!storedEncName) + return XML_ERROR_NO_MEMORY; + } + result = handleUnknownEncoding(parser, storedEncName); + poolClear(&temp2Pool); + if (result == XML_ERROR_UNKNOWN_ENCODING) + eventPtr = encodingName; + return result; + } + } + + if (storedEncName || storedversion) + poolClear(&temp2Pool); + + return XML_ERROR_NONE; +} + +static enum XML_Error +handleUnknownEncoding(XML_Parser parser, const XML_Char *encodingName) +{ + if (unknownEncodingHandler) { + XML_Encoding info; + int i; + for (i = 0; i < 256; i++) + info.map[i] = -1; + info.convert = NULL; + info.data = NULL; + info.release = NULL; + if (unknownEncodingHandler(unknownEncodingHandlerData, encodingName, + &info)) { + ENCODING *enc; + unknownEncodingMem = MALLOC(XmlSizeOfUnknownEncoding()); + if (!unknownEncodingMem) { + if (info.release) + info.release(info.data); + return XML_ERROR_NO_MEMORY; + } + enc = (ns + ? XmlInitUnknownEncodingNS + : XmlInitUnknownEncoding)(unknownEncodingMem, + info.map, + info.convert, + info.data); + if (enc) { + unknownEncodingData = info.data; + unknownEncodingRelease = info.release; + encoding = enc; + return XML_ERROR_NONE; + } + } + if (info.release != NULL) + info.release(info.data); + } + return XML_ERROR_UNKNOWN_ENCODING; +} + +static enum XML_Error PTRCALL +prologInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + processor = prologProcessor; + return prologProcessor(parser, s, end, nextPtr); +} + +#ifdef XML_DTD + +static enum XML_Error PTRCALL +externalParEntInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + enum XML_Error result = initializeEncoding(parser); + if (result != XML_ERROR_NONE) + return result; + + /* we know now that XML_Parse(Buffer) has been called, + so we consider the external parameter entity read */ + _dtd->paramEntityRead = XML_TRUE; + + if (prologState.inEntityValue) { + processor = entityValueInitProcessor; + return entityValueInitProcessor(parser, s, end, nextPtr); + } + else { + processor = externalParEntProcessor; + return externalParEntProcessor(parser, s, end, nextPtr); + } +} + +static enum XML_Error PTRCALL +entityValueInitProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + int tok; + const char *start = s; + const char *next = start; + eventPtr = start; + + for (;;) { + tok = XmlPrologTok(encoding, start, end, &next); + eventEndPtr = next; + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + /* found end of entity value - can store it now */ + return storeEntityValue(parser, encoding, s, end); + } + else if (tok == XML_TOK_XML_DECL) { + enum XML_Error result; + result = processXmlDecl(parser, 0, start, next); + if (result != XML_ERROR_NONE) + return result; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + *nextPtr = next; + } + /* stop scanning for text declaration - we found one */ + processor = entityValueProcessor; + return entityValueProcessor(parser, next, end, nextPtr); + } + /* If we are at the end of the buffer, this would cause XmlPrologTok to + return XML_TOK_NONE on the next call, which would then cause the + function to exit with *nextPtr set to s - that is what we want for other + tokens, but not for the BOM - we would rather like to skip it; + then, when this routine is entered the next time, XmlPrologTok will + return XML_TOK_INVALID, since the BOM is still in the buffer + */ + else if (tok == XML_TOK_BOM && next == end && !ps_finalBuffer) { + *nextPtr = next; + return XML_ERROR_NONE; + } + start = next; + eventPtr = start; + } +} + +static enum XML_Error PTRCALL +externalParEntProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok; + + tok = XmlPrologTok(encoding, s, end, &next); + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + } + /* This would cause the next stage, i.e. doProlog to be passed XML_TOK_BOM. + However, when parsing an external subset, doProlog will not accept a BOM + as valid, and report a syntax error, so we have to skip the BOM + */ + else if (tok == XML_TOK_BOM) { + s = next; + tok = XmlPrologTok(encoding, s, end, &next); + } + + processor = prologProcessor; + return doProlog(parser, encoding, s, end, tok, next, + nextPtr, (XML_Bool)!ps_finalBuffer); +} + +static enum XML_Error PTRCALL +entityValueProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *start = s; + const char *next = s; + const ENCODING *enc = encoding; + int tok; + + for (;;) { + tok = XmlPrologTok(enc, start, end, &next); + if (tok <= 0) { + if (!ps_finalBuffer && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case XML_TOK_NONE: /* start == end */ + default: + break; + } + /* found end of entity value - can store it now */ + return storeEntityValue(parser, enc, s, end); + } + start = next; + } +} + +#endif /* XML_DTD */ + +static enum XML_Error PTRCALL +prologProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + const char *next = s; + int tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, + nextPtr, (XML_Bool)!ps_finalBuffer); +} + +static enum XML_Error +doProlog(XML_Parser parser, + const ENCODING *enc, + const char *s, + const char *end, + int tok, + const char *next, + const char **nextPtr, + XML_Bool haveMore) +{ +#ifdef XML_DTD + static const XML_Char externalSubsetName[] = { ASCII_HASH , '\0' }; +#endif /* XML_DTD */ + static const XML_Char atypeCDATA[] = + { ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; + static const XML_Char atypeID[] = { ASCII_I, ASCII_D, '\0' }; + static const XML_Char atypeIDREF[] = + { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; + static const XML_Char atypeIDREFS[] = + { ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; + static const XML_Char atypeENTITY[] = + { ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; + static const XML_Char atypeENTITIES[] = { ASCII_E, ASCII_N, + ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, '\0' }; + static const XML_Char atypeNMTOKEN[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; + static const XML_Char atypeNMTOKENS[] = { ASCII_N, ASCII_M, ASCII_T, + ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, '\0' }; + static const XML_Char notationPrefix[] = { ASCII_N, ASCII_O, ASCII_T, + ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, ASCII_LPAREN, '\0' }; + static const XML_Char enumValueSep[] = { ASCII_PIPE, '\0' }; + static const XML_Char enumValueStart[] = { ASCII_LPAREN, '\0' }; + + /* save one level of indirection */ + DTD * const dtd = _dtd; + + const char **eventPP; + const char **eventEndPP; + enum XML_Content_Quant quant; + + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + + for (;;) { + int role; + XML_Bool handleDefault = XML_TRUE; + *eventPP = s; + *eventEndPP = next; + if (tok <= 0) { + if (haveMore && tok != XML_TOK_INVALID) { + *nextPtr = s; + return XML_ERROR_NONE; + } + switch (tok) { + case XML_TOK_INVALID: + *eventPP = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + return XML_ERROR_PARTIAL_CHAR; + case -XML_TOK_PROLOG_S: + tok = -tok; + break; + case XML_TOK_NONE: +#ifdef XML_DTD + /* for internal PE NOT referenced between declarations */ + if (enc != encoding && !openInternalEntities->betweenDecl) { + *nextPtr = s; + return XML_ERROR_NONE; + } + /* WFC: PE Between Declarations - must check that PE contains + complete markup, not only for external PEs, but also for + internal PEs if the reference occurs between declarations. + */ + if (isParamEntity || enc != encoding) { + if (XmlTokenRole(&prologState, XML_TOK_NONE, end, end, enc) + == XML_ROLE_ERROR) + return XML_ERROR_INCOMPLETE_PE; + *nextPtr = s; + return XML_ERROR_NONE; + } +#endif /* XML_DTD */ + return XML_ERROR_NO_ELEMENTS; + default: + tok = -tok; + next = end; + break; + } + } + role = XmlTokenRole(&prologState, tok, s, next, enc); + switch (role) { + case XML_ROLE_XML_DECL: + { + enum XML_Error result = processXmlDecl(parser, 0, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_NAME: + if (startDoctypeDeclHandler) { + doctypeName = poolStoreString(&tempPool, enc, s, next); + if (!doctypeName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + doctypePubid = NULL; + handleDefault = XML_FALSE; + } + doctypeSysid = NULL; /* always initialize to NULL */ + break; + case XML_ROLE_DOCTYPE_INTERNAL_SUBSET: + if (startDoctypeDeclHandler) { + startDoctypeDeclHandler(handlerArg, doctypeName, doctypeSysid, + doctypePubid, 1); + doctypeName = NULL; + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + break; +#ifdef XML_DTD + case XML_ROLE_TEXT_DECL: + { + enum XML_Error result = processXmlDecl(parser, 1, s, next); + if (result != XML_ERROR_NONE) + return result; + enc = encoding; + handleDefault = XML_FALSE; + } + break; +#endif /* XML_DTD */ + case XML_ROLE_DOCTYPE_PUBLIC_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; + declEntity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + XML_Char *pubId; + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + pubId = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!pubId) + return XML_ERROR_NO_MEMORY; + normalizePublicId(pubId); + poolFinish(&tempPool); + doctypePubid = pubId; + handleDefault = XML_FALSE; + goto alreadyChecked; + } + /* fall through */ + case XML_ROLE_ENTITY_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + alreadyChecked: + if (dtd->keepProcessing && declEntity) { + XML_Char *tem = poolStoreString(&dtd->pool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declEntity->publicId = tem; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_DOCTYPE_CLOSE: + if (doctypeName) { + startDoctypeDeclHandler(handlerArg, doctypeName, + doctypeSysid, doctypePubid, 0); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + /* doctypeSysid will be non-NULL in the case of a previous + XML_ROLE_DOCTYPE_SYSTEM_ID, even if startDoctypeDeclHandler + was not set, indicating an external subset + */ +#ifdef XML_DTD + if (doctypeSysid || useForeignDTD) { + XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + if (useForeignDTD) + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead) { + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + /* if we didn't read the foreign DTD then this means that there + is no external subset and we must reset dtd->hasParamEntityRefs + */ + else if (!doctypeSysid) + dtd->hasParamEntityRefs = hadParamEntityRefs; + /* end of DTD - no need to update dtd->keepProcessing */ + } + useForeignDTD = XML_FALSE; + } +#endif /* XML_DTD */ + if (endDoctypeDeclHandler) { + endDoctypeDeclHandler(handlerArg); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_INSTANCE_START: +#ifdef XML_DTD + /* if there is no DOCTYPE declaration then now is the + last chance to read the foreign DTD + */ + if (useForeignDTD) { + XML_Bool hadParamEntityRefs = dtd->hasParamEntityRefs; + dtd->hasParamEntityRefs = XML_TRUE; + if (paramEntityParsing && externalEntityRefHandler) { + ENTITY *entity = (ENTITY *)lookup(parser, &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!entity) + return XML_ERROR_NO_MEMORY; + entity->base = curBase; + dtd->paramEntityRead = XML_FALSE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + if (dtd->paramEntityRead) { + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + } + /* if we didn't read the foreign DTD then this means that there + is no external subset and we must reset dtd->hasParamEntityRefs + */ + else + dtd->hasParamEntityRefs = hadParamEntityRefs; + /* end of DTD - no need to update dtd->keepProcessing */ + } + } +#endif /* XML_DTD */ + processor = contentProcessor; + return contentProcessor(parser, s, end, nextPtr); + case XML_ROLE_ATTLIST_ELEMENT_NAME: + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_NAME: + declAttributeId = getAttributeId(parser, enc, s, next); + if (!declAttributeId) + return XML_ERROR_NO_MEMORY; + declAttributeIsCdata = XML_FALSE; + declAttributeType = NULL; + declAttributeIsId = XML_FALSE; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_CDATA: + declAttributeIsCdata = XML_TRUE; + declAttributeType = atypeCDATA; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ID: + declAttributeIsId = XML_TRUE; + declAttributeType = atypeID; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREF: + declAttributeType = atypeIDREF; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_IDREFS: + declAttributeType = atypeIDREFS; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITY: + declAttributeType = atypeENTITY; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_ENTITIES: + declAttributeType = atypeENTITIES; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN: + declAttributeType = atypeNMTOKEN; + goto checkAttListDeclHandler; + case XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS: + declAttributeType = atypeNMTOKENS; + checkAttListDeclHandler: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTRIBUTE_ENUM_VALUE: + case XML_ROLE_ATTRIBUTE_NOTATION_VALUE: + if (dtd->keepProcessing && attlistDeclHandler) { + const XML_Char *prefix; + if (declAttributeType) { + prefix = enumValueSep; + } + else { + prefix = (role == XML_ROLE_ATTRIBUTE_NOTATION_VALUE + ? notationPrefix + : enumValueStart); + } + if (!poolAppendString(&tempPool, prefix)) + return XML_ERROR_NO_MEMORY; + if (!poolAppend(&tempPool, enc, s, next)) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_IMPLIED_ATTRIBUTE_VALUE: + case XML_ROLE_REQUIRED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, declAttributeIsId, + 0, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T(ASCII_LPAREN) + || (*declAttributeType == XML_T(ASCII_N) + && declAttributeType[1] == XML_T(ASCII_O))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + 0, role == XML_ROLE_REQUIRED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_DEFAULT_ATTRIBUTE_VALUE: + case XML_ROLE_FIXED_ATTRIBUTE_VALUE: + if (dtd->keepProcessing) { + const XML_Char *attVal; + enum XML_Error result = + storeAttributeValue(parser, enc, declAttributeIsCdata, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar, + &dtd->pool); + if (result) + return result; + attVal = poolStart(&dtd->pool); + poolFinish(&dtd->pool); + /* ID attributes aren't allowed to have a default */ + if (!defineAttribute(declElementType, declAttributeId, + declAttributeIsCdata, XML_FALSE, attVal, parser)) + return XML_ERROR_NO_MEMORY; + if (attlistDeclHandler && declAttributeType) { + if (*declAttributeType == XML_T(ASCII_LPAREN) + || (*declAttributeType == XML_T(ASCII_N) + && declAttributeType[1] == XML_T(ASCII_O))) { + /* Enumerated or Notation type */ + if (!poolAppendChar(&tempPool, XML_T(ASCII_RPAREN)) + || !poolAppendChar(&tempPool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + declAttributeType = tempPool.start; + poolFinish(&tempPool); + } + *eventEndPP = s; + attlistDeclHandler(handlerArg, declElementType->name, + declAttributeId->name, declAttributeType, + attVal, + role == XML_ROLE_FIXED_ATTRIBUTE_VALUE); + poolClear(&tempPool); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_ENTITY_VALUE: + if (dtd->keepProcessing) { + enum XML_Error result = storeEntityValue(parser, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (declEntity) { + declEntity->textPtr = poolStart(&dtd->entityValuePool); + declEntity->textLen = (int)(poolLength(&dtd->entityValuePool)); + poolFinish(&dtd->entityValuePool); + if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + declEntity->textPtr, + declEntity->textLen, + curBase, 0, 0, 0); + handleDefault = XML_FALSE; + } + } + else + poolDiscard(&dtd->entityValuePool); + if (result != XML_ERROR_NONE) + return result; + } + break; + case XML_ROLE_DOCTYPE_SYSTEM_ID: +#ifdef XML_DTD + useForeignDTD = XML_FALSE; +#endif /* XML_DTD */ + dtd->hasParamEntityRefs = XML_TRUE; + if (startDoctypeDeclHandler) { + doctypeSysid = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (doctypeSysid == NULL) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } +#ifdef XML_DTD + else + /* use externalSubsetName to make doctypeSysid non-NULL + for the case where no startDoctypeDeclHandler is set */ + doctypeSysid = externalSubsetName; +#endif /* XML_DTD */ + if (!dtd->standalone +#ifdef XML_DTD + && !paramEntityParsing +#endif /* XML_DTD */ + && notStandaloneHandler + && !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; +#ifndef XML_DTD + break; +#else /* XML_DTD */ + if (!declEntity) { + declEntity = (ENTITY *)lookup(parser, + &dtd->paramEntities, + externalSubsetName, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + declEntity->publicId = NULL; + } + /* fall through */ +#endif /* XML_DTD */ + case XML_ROLE_ENTITY_SYSTEM_ID: + if (dtd->keepProcessing && declEntity) { + declEntity->systemId = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!declEntity->systemId) + return XML_ERROR_NO_MEMORY; + declEntity->base = curBase; + poolFinish(&dtd->pool); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_COMPLETE: + if (dtd->keepProcessing && declEntity && entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + declEntity->is_param, + 0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + 0); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_ENTITY_NOTATION_NAME: + if (dtd->keepProcessing && declEntity) { + declEntity->notation = poolStoreString(&dtd->pool, enc, s, next); + if (!declEntity->notation) + return XML_ERROR_NO_MEMORY; + poolFinish(&dtd->pool); + if (unparsedEntityDeclHandler) { + *eventEndPP = s; + unparsedEntityDeclHandler(handlerArg, + declEntity->name, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + else if (entityDeclHandler) { + *eventEndPP = s; + entityDeclHandler(handlerArg, + declEntity->name, + 0,0,0, + declEntity->base, + declEntity->systemId, + declEntity->publicId, + declEntity->notation); + handleDefault = XML_FALSE; + } + } + break; + case XML_ROLE_GENERAL_ENTITY_NAME: + { + if (XmlPredefinedEntityName(enc, s, next)) { + declEntity = NULL; + break; + } + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, + sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_FALSE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + } + break; + case XML_ROLE_PARAM_ENTITY_NAME: +#ifdef XML_DTD + if (dtd->keepProcessing) { + const XML_Char *name = poolStoreString(&dtd->pool, enc, s, next); + if (!name) + return XML_ERROR_NO_MEMORY; + declEntity = (ENTITY *)lookup(parser, &dtd->paramEntities, + name, sizeof(ENTITY)); + if (!declEntity) + return XML_ERROR_NO_MEMORY; + if (declEntity->name != name) { + poolDiscard(&dtd->pool); + declEntity = NULL; + } + else { + poolFinish(&dtd->pool); + declEntity->publicId = NULL; + declEntity->is_param = XML_TRUE; + /* if we have a parent parser or are reading an internal parameter + entity, then the entity declaration is not considered "internal" + */ + declEntity->is_internal = !(parentParser || openInternalEntities); + if (entityDeclHandler) + handleDefault = XML_FALSE; + } + } + else { + poolDiscard(&dtd->pool); + declEntity = NULL; + } +#else /* not XML_DTD */ + declEntity = NULL; +#endif /* XML_DTD */ + break; + case XML_ROLE_NOTATION_NAME: + declNotationPublicId = NULL; + declNotationName = NULL; + if (notationDeclHandler) { + declNotationName = poolStoreString(&tempPool, enc, s, next); + if (!declNotationName) + return XML_ERROR_NO_MEMORY; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_PUBLIC_ID: + if (!XmlIsPublicId(enc, s, next, eventPP)) + return XML_ERROR_PUBLICID; + if (declNotationName) { /* means notationDeclHandler != NULL */ + XML_Char *tem = poolStoreString(&tempPool, + enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!tem) + return XML_ERROR_NO_MEMORY; + normalizePublicId(tem); + declNotationPublicId = tem; + poolFinish(&tempPool); + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_NOTATION_SYSTEM_ID: + if (declNotationName && notationDeclHandler) { + const XML_Char *systemId + = poolStoreString(&tempPool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!systemId) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + systemId, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_NOTATION_NO_SYSTEM_ID: + if (declNotationPublicId && notationDeclHandler) { + *eventEndPP = s; + notationDeclHandler(handlerArg, + declNotationName, + curBase, + 0, + declNotationPublicId); + handleDefault = XML_FALSE; + } + poolClear(&tempPool); + break; + case XML_ROLE_ERROR: + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: + /* PE references in internal subset are + not allowed within declarations. */ + return XML_ERROR_PARAM_ENTITY_REF; + case XML_TOK_XML_DECL: + return XML_ERROR_MISPLACED_XML_PI; + default: + return XML_ERROR_SYNTAX; + } +#ifdef XML_DTD + case XML_ROLE_IGNORE_SECT: + { + enum XML_Error result; + if (defaultHandler) + reportDefault(parser, enc, s, next); + handleDefault = XML_FALSE; + result = doIgnoreSection(parser, enc, &next, end, nextPtr, haveMore); + if (result != XML_ERROR_NONE) + return result; + else if (!next) { + processor = ignoreSectionProcessor; + return result; + } + } + break; +#endif /* XML_DTD */ + case XML_ROLE_GROUP_OPEN: + if (prologState.level >= groupSize) { + if (groupSize) { + char *temp = (char *)REALLOC(groupConnector, groupSize *= 2); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + groupConnector = temp; + if (dtd->scaffIndex) { + int *temp = (int *)REALLOC(dtd->scaffIndex, + groupSize * sizeof(int)); + if (temp == NULL) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex = temp; + } + } + else { + groupConnector = (char *)MALLOC(groupSize = 32); + if (!groupConnector) + return XML_ERROR_NO_MEMORY; + } + } + groupConnector[prologState.level] = 0; + if (dtd->in_eldecl) { + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffIndex[dtd->scaffLevel] = myindex; + dtd->scaffLevel++; + dtd->scaffold[myindex].type = XML_CTYPE_SEQ; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + case XML_ROLE_GROUP_SEQUENCE: + if (groupConnector[prologState.level] == ASCII_PIPE) + return XML_ERROR_SYNTAX; + groupConnector[prologState.level] = ASCII_COMMA; + if (dtd->in_eldecl && elementDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_GROUP_CHOICE: + if (groupConnector[prologState.level] == ASCII_COMMA) + return XML_ERROR_SYNTAX; + if (dtd->in_eldecl + && !groupConnector[prologState.level] + && (dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + != XML_CTYPE_MIXED) + ) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_CHOICE; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + groupConnector[prologState.level] = ASCII_PIPE; + break; + case XML_ROLE_PARAM_ENTITY_REF: +#ifdef XML_DTD + case XML_ROLE_INNER_PARAM_ENTITY_REF: + dtd->hasParamEntityRefs = XML_TRUE; + if (!paramEntityParsing) + dtd->keepProcessing = dtd->standalone; + else { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&dtd->pool, enc, + s + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); + poolDiscard(&dtd->pool); + /* first, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal, + otherwise call the skipped entity handler + */ + if (prologState.documentEntity && + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs)) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + dtd->keepProcessing = dtd->standalone; + /* cannot report skipped entities in declarations */ + if ((role == XML_ROLE_PARAM_ENTITY_REF) && skippedEntityHandler) { + skippedEntityHandler(handlerArg, name, 1); + handleDefault = XML_FALSE; + } + break; + } + if (entity->open) + return XML_ERROR_RECURSIVE_ENTITY_REF; + if (entity->textPtr) { + enum XML_Error result; + XML_Bool betweenDecl = + (role == XML_ROLE_PARAM_ENTITY_REF ? XML_TRUE : XML_FALSE); + result = processInternalEntity(parser, entity, betweenDecl); + if (result != XML_ERROR_NONE) + return result; + handleDefault = XML_FALSE; + break; + } + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + return XML_ERROR_EXTERNAL_ENTITY_HANDLING; + } + entity->open = XML_FALSE; + handleDefault = XML_FALSE; + if (!dtd->paramEntityRead) { + dtd->keepProcessing = dtd->standalone; + break; + } + } + else { + dtd->keepProcessing = dtd->standalone; + break; + } + } +#endif /* XML_DTD */ + if (!dtd->standalone && + notStandaloneHandler && + !notStandaloneHandler(handlerArg)) + return XML_ERROR_NOT_STANDALONE; + break; + + /* Element declaration stuff */ + + case XML_ROLE_ELEMENT_NAME: + if (elementDeclHandler) { + declElementType = getElementType(parser, enc, s, next); + if (!declElementType) + return XML_ERROR_NO_MEMORY; + dtd->scaffLevel = 0; + dtd->scaffCount = 0; + dtd->in_eldecl = XML_TRUE; + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ANY: + case XML_ROLE_CONTENT_EMPTY: + if (dtd->in_eldecl) { + if (elementDeclHandler) { + XML_Content * content = (XML_Content *) MALLOC(sizeof(XML_Content)); + if (!content) + return XML_ERROR_NO_MEMORY; + content->quant = XML_CQUANT_NONE; + content->name = NULL; + content->numchildren = 0; + content->children = NULL; + content->type = ((role == XML_ROLE_CONTENT_ANY) ? + XML_CTYPE_ANY : + XML_CTYPE_EMPTY); + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, content); + handleDefault = XML_FALSE; + } + dtd->in_eldecl = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_PCDATA: + if (dtd->in_eldecl) { + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel - 1]].type + = XML_CTYPE_MIXED; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_CONTENT_ELEMENT: + quant = XML_CQUANT_NONE; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_OPT: + quant = XML_CQUANT_OPT; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_REP: + quant = XML_CQUANT_REP; + goto elementContent; + case XML_ROLE_CONTENT_ELEMENT_PLUS: + quant = XML_CQUANT_PLUS; + elementContent: + if (dtd->in_eldecl) { + ELEMENT_TYPE *el; + const XML_Char *name; + int nameLen; + const char *nxt = (quant == XML_CQUANT_NONE + ? next + : next - enc->minBytesPerChar); + int myindex = nextScaffoldPart(parser); + if (myindex < 0) + return XML_ERROR_NO_MEMORY; + dtd->scaffold[myindex].type = XML_CTYPE_NAME; + dtd->scaffold[myindex].quant = quant; + el = getElementType(parser, enc, s, nxt); + if (!el) + return XML_ERROR_NO_MEMORY; + name = el->name; + dtd->scaffold[myindex].name = name; + nameLen = 0; + for (; name[nameLen++]; ); + dtd->contentStringLen += nameLen; + if (elementDeclHandler) + handleDefault = XML_FALSE; + } + break; + + case XML_ROLE_GROUP_CLOSE: + quant = XML_CQUANT_NONE; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_OPT: + quant = XML_CQUANT_OPT; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_REP: + quant = XML_CQUANT_REP; + goto closeGroup; + case XML_ROLE_GROUP_CLOSE_PLUS: + quant = XML_CQUANT_PLUS; + closeGroup: + if (dtd->in_eldecl) { + if (elementDeclHandler) + handleDefault = XML_FALSE; + dtd->scaffLevel--; + dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel]].quant = quant; + if (dtd->scaffLevel == 0) { + if (!handleDefault) { + XML_Content *model = build_model(parser); + if (!model) + return XML_ERROR_NO_MEMORY; + *eventEndPP = s; + elementDeclHandler(handlerArg, declElementType->name, model); + } + dtd->in_eldecl = XML_FALSE; + dtd->contentStringLen = 0; + } + } + break; + /* End element declaration stuff */ + + case XML_ROLE_PI: + if (!reportProcessingInstruction(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_COMMENT: + if (!reportComment(parser, enc, s, next)) + return XML_ERROR_NO_MEMORY; + handleDefault = XML_FALSE; + break; + case XML_ROLE_NONE: + switch (tok) { + case XML_TOK_BOM: + handleDefault = XML_FALSE; + break; + } + break; + case XML_ROLE_DOCTYPE_NONE: + if (startDoctypeDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ENTITY_NONE: + if (dtd->keepProcessing && entityDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_NOTATION_NONE: + if (notationDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ATTLIST_NONE: + if (dtd->keepProcessing && attlistDeclHandler) + handleDefault = XML_FALSE; + break; + case XML_ROLE_ELEMENT_NONE: + if (elementDeclHandler) + handleDefault = XML_FALSE; + break; + } /* end of big switch */ + + if (handleDefault && defaultHandler) + reportDefault(parser, enc, s, next); + + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: + s = next; + tok = XmlPrologTok(enc, s, end, &next); + } + } + /* not reached */ +} + +static enum XML_Error PTRCALL +epilogProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + processor = epilogProcessor; + eventPtr = s; + for (;;) { + const char *next = NULL; + int tok = XmlPrologTok(encoding, s, end, &next); + eventEndPtr = next; + switch (tok) { + /* report partial linebreak - it might be the last token */ + case -XML_TOK_PROLOG_S: + if (defaultHandler) { + reportDefault(parser, encoding, s, next); + if (ps_parsing == XML_FINISHED) + return XML_ERROR_ABORTED; + } + *nextPtr = next; + return XML_ERROR_NONE; + case XML_TOK_NONE: + *nextPtr = s; + return XML_ERROR_NONE; + case XML_TOK_PROLOG_S: + if (defaultHandler) + reportDefault(parser, encoding, s, next); + break; + case XML_TOK_PI: + if (!reportProcessingInstruction(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_COMMENT: + if (!reportComment(parser, encoding, s, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_INVALID: + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (!ps_finalBuffer) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_UNCLOSED_TOKEN; + case XML_TOK_PARTIAL_CHAR: + if (!ps_finalBuffer) { + *nextPtr = s; + return XML_ERROR_NONE; + } + return XML_ERROR_PARTIAL_CHAR; + default: + return XML_ERROR_JUNK_AFTER_DOC_ELEMENT; + } + eventPtr = s = next; + switch (ps_parsing) { + case XML_SUSPENDED: + *nextPtr = next; + return XML_ERROR_NONE; + case XML_FINISHED: + return XML_ERROR_ABORTED; + default: ; + } + } +} + +static enum XML_Error +processInternalEntity(XML_Parser parser, ENTITY *entity, + XML_Bool betweenDecl) +{ + const char *textStart, *textEnd; + const char *next; + enum XML_Error result; + OPEN_INTERNAL_ENTITY *openEntity; + + if (freeInternalEntities) { + openEntity = freeInternalEntities; + freeInternalEntities = openEntity->next; + } + else { + openEntity = (OPEN_INTERNAL_ENTITY *)MALLOC(sizeof(OPEN_INTERNAL_ENTITY)); + if (!openEntity) + return XML_ERROR_NO_MEMORY; + } + entity->open = XML_TRUE; + entity->processed = 0; + openEntity->next = openInternalEntities; + openInternalEntities = openEntity; + openEntity->entity = entity; + openEntity->startTagLevel = tagLevel; + openEntity->betweenDecl = betweenDecl; + openEntity->internalEventPtr = NULL; + openEntity->internalEventEndPtr = NULL; + textStart = (char *)entity->textPtr; + textEnd = (char *)(entity->textPtr + entity->textLen); + +#ifdef XML_DTD + if (entity->is_param) { + int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, internalEncoding, textStart, textEnd, tok, + next, &next, XML_FALSE); + } + else +#endif /* XML_DTD */ + result = doContent(parser, tagLevel, internalEncoding, textStart, + textEnd, &next, XML_FALSE); + + if (result == XML_ERROR_NONE) { + if (textEnd != next && ps_parsing == XML_SUSPENDED) { + entity->processed = (int)(next - textStart); + processor = internalEntityProcessor; + } + else { + entity->open = XML_FALSE; + openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + } + return result; +} + +static enum XML_Error PTRCALL +internalEntityProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + ENTITY *entity; + const char *textStart, *textEnd; + const char *next; + enum XML_Error result; + OPEN_INTERNAL_ENTITY *openEntity = openInternalEntities; + if (!openEntity) + return XML_ERROR_UNEXPECTED_STATE; + + entity = openEntity->entity; + textStart = ((char *)entity->textPtr) + entity->processed; + textEnd = (char *)(entity->textPtr + entity->textLen); + +#ifdef XML_DTD + if (entity->is_param) { + int tok = XmlPrologTok(internalEncoding, textStart, textEnd, &next); + result = doProlog(parser, internalEncoding, textStart, textEnd, tok, + next, &next, XML_FALSE); + } + else +#endif /* XML_DTD */ + result = doContent(parser, openEntity->startTagLevel, internalEncoding, + textStart, textEnd, &next, XML_FALSE); + + if (result != XML_ERROR_NONE) + return result; + else if (textEnd != next && ps_parsing == XML_SUSPENDED) { + entity->processed = (int)(next - (char *)entity->textPtr); + return result; + } + else { + entity->open = XML_FALSE; + openInternalEntities = openEntity->next; + /* put openEntity back in list of free instances */ + openEntity->next = freeInternalEntities; + freeInternalEntities = openEntity; + } + +#ifdef XML_DTD + if (entity->is_param) { + int tok; + processor = prologProcessor; + tok = XmlPrologTok(encoding, s, end, &next); + return doProlog(parser, encoding, s, end, tok, next, nextPtr, + (XML_Bool)!ps_finalBuffer); + } + else +#endif /* XML_DTD */ + { + processor = contentProcessor; + /* see externalEntityContentProcessor vs contentProcessor */ + return doContent(parser, parentParser ? 1 : 0, encoding, s, end, + nextPtr, (XML_Bool)!ps_finalBuffer); + } +} + +static enum XML_Error PTRCALL +errorProcessor(XML_Parser parser, + const char *s, + const char *end, + const char **nextPtr) +{ + return errorCode; +} + +static enum XML_Error +storeAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + enum XML_Error result = appendAttributeValue(parser, enc, isCdata, ptr, + end, pool); + if (result) + return result; + if (!isCdata && poolLength(pool) && poolLastChar(pool) == 0x20) + poolChop(pool); + if (!poolAppendChar(pool, XML_T('\0'))) + return XML_ERROR_NO_MEMORY; + return XML_ERROR_NONE; +} + +static enum XML_Error +appendAttributeValue(XML_Parser parser, const ENCODING *enc, XML_Bool isCdata, + const char *ptr, const char *end, + STRING_POOL *pool) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + for (;;) { + const char *next; + int tok = XmlAttributeValueTok(enc, ptr, end, &next); + switch (tok) { + case XML_TOK_NONE: + return XML_ERROR_NONE; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_INVALID_TOKEN; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, ptr); + if (n < 0) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + if (!isCdata + && n == 0x20 /* space */ + && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BAD_CHAR_REF; + } + for (i = 0; i < n; i++) { + if (!poolAppendChar(pool, buf[i])) + return XML_ERROR_NO_MEMORY; + } + } + break; + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, ptr, next)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_TRAILING_CR: + next = ptr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_ATTRIBUTE_VALUE_S: + case XML_TOK_DATA_NEWLINE: + if (!isCdata && (poolLength(pool) == 0 || poolLastChar(pool) == 0x20)) + break; + if (!poolAppendChar(pool, 0x20)) + return XML_ERROR_NO_MEMORY; + break; + case XML_TOK_ENTITY_REF: + { + const XML_Char *name; + ENTITY *entity; + char checkEntityDecl; + XML_Char ch = (XML_Char) XmlPredefinedEntityName(enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (ch) { + if (!poolAppendChar(pool, ch)) + return XML_ERROR_NO_MEMORY; + break; + } + name = poolStoreString(&temp2Pool, enc, + ptr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) + return XML_ERROR_NO_MEMORY; + entity = (ENTITY *)lookup(parser, &dtd->generalEntities, name, 0); + poolDiscard(&temp2Pool); + /* First, determine if a check for an existing declaration is needed; + if yes, check that the entity exists, and that it is internal. + */ + if (pool == &dtd->pool) /* are we called from prolog? */ + checkEntityDecl = +#ifdef XML_DTD + prologState.documentEntity && +#endif /* XML_DTD */ + (dtd->standalone + ? !openInternalEntities + : !dtd->hasParamEntityRefs); + else /* if (pool == &tempPool): we are called from content */ + checkEntityDecl = !dtd->hasParamEntityRefs || dtd->standalone; + if (checkEntityDecl) { + if (!entity) + return XML_ERROR_UNDEFINED_ENTITY; + else if (!entity->is_internal) + return XML_ERROR_ENTITY_DECLARED_IN_PE; + } + else if (!entity) { + /* Cannot report skipped entity here - see comments on + skippedEntityHandler. + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + /* Cannot call the default handler because this would be + out of sync with the call to the startElementHandler. + if ((pool == &tempPool) && defaultHandler) + reportDefault(parser, enc, ptr, next); + */ + break; + } + if (entity->open) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_RECURSIVE_ENTITY_REF; + } + if (entity->notation) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_BINARY_ENTITY_REF; + } + if (!entity->textPtr) { + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF; + } + else { + enum XML_Error result; + const XML_Char *textEnd = entity->textPtr + entity->textLen; + entity->open = XML_TRUE; + result = appendAttributeValue(parser, internalEncoding, isCdata, + (char *)entity->textPtr, + (char *)textEnd, pool); + entity->open = XML_FALSE; + if (result) + return result; + } + } + break; + default: + if (enc == encoding) + eventPtr = ptr; + return XML_ERROR_UNEXPECTED_STATE; + } + ptr = next; + } + /* not reached */ +} + +static enum XML_Error +storeEntityValue(XML_Parser parser, + const ENCODING *enc, + const char *entityTextPtr, + const char *entityTextEnd) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + STRING_POOL *pool = &(dtd->entityValuePool); + enum XML_Error result = XML_ERROR_NONE; +#ifdef XML_DTD + int oldInEntityValue = prologState.inEntityValue; + prologState.inEntityValue = 1; +#endif /* XML_DTD */ + /* never return Null for the value argument in EntityDeclHandler, + since this would indicate an external entity; therefore we + have to make sure that entityValuePool.start is not null */ + if (!pool->blocks) { + if (!poolGrow(pool)) + return XML_ERROR_NO_MEMORY; + } + + for (;;) { + const char *next; + int tok = XmlEntityValueTok(enc, entityTextPtr, entityTextEnd, &next); + switch (tok) { + case XML_TOK_PARAM_ENTITY_REF: +#ifdef XML_DTD + if (isParamEntity || enc != encoding) { + const XML_Char *name; + ENTITY *entity; + name = poolStoreString(&tempPool, enc, + entityTextPtr + enc->minBytesPerChar, + next - enc->minBytesPerChar); + if (!name) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + entity = (ENTITY *)lookup(parser, &dtd->paramEntities, name, 0); + poolDiscard(&tempPool); + if (!entity) { + /* not a well-formedness error - see XML 1.0: WFC Entity Declared */ + /* cannot report skipped entity here - see comments on + skippedEntityHandler + if (skippedEntityHandler) + skippedEntityHandler(handlerArg, name, 0); + */ + dtd->keepProcessing = dtd->standalone; + goto endEntityValue; + } + if (entity->open) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_RECURSIVE_ENTITY_REF; + goto endEntityValue; + } + if (entity->systemId) { + if (externalEntityRefHandler) { + dtd->paramEntityRead = XML_FALSE; + entity->open = XML_TRUE; + if (!externalEntityRefHandler(externalEntityRefHandlerArg, + 0, + entity->base, + entity->systemId, + entity->publicId)) { + entity->open = XML_FALSE; + result = XML_ERROR_EXTERNAL_ENTITY_HANDLING; + goto endEntityValue; + } + entity->open = XML_FALSE; + if (!dtd->paramEntityRead) + dtd->keepProcessing = dtd->standalone; + } + else + dtd->keepProcessing = dtd->standalone; + } + else { + entity->open = XML_TRUE; + result = storeEntityValue(parser, + internalEncoding, + (char *)entity->textPtr, + (char *)(entity->textPtr + + entity->textLen)); + entity->open = XML_FALSE; + if (result) + goto endEntityValue; + } + break; + } +#endif /* XML_DTD */ + /* In the internal subset, PE references are not legal + within markup declarations, e.g entity values in this case. */ + eventPtr = entityTextPtr; + result = XML_ERROR_PARAM_ENTITY_REF; + goto endEntityValue; + case XML_TOK_NONE: + result = XML_ERROR_NONE; + goto endEntityValue; + case XML_TOK_ENTITY_REF: + case XML_TOK_DATA_CHARS: + if (!poolAppend(pool, enc, entityTextPtr, next)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + break; + case XML_TOK_TRAILING_CR: + next = entityTextPtr + enc->minBytesPerChar; + /* fall through */ + case XML_TOK_DATA_NEWLINE: + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = 0xA; + break; + case XML_TOK_CHAR_REF: + { + XML_Char buf[XML_ENCODE_MAX]; + int i; + int n = XmlCharRefNumber(enc, entityTextPtr); + if (n < 0) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + n = XmlEncode(n, (ICHAR *)buf); + if (!n) { + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_BAD_CHAR_REF; + goto endEntityValue; + } + for (i = 0; i < n; i++) { + if (pool->end == pool->ptr && !poolGrow(pool)) { + result = XML_ERROR_NO_MEMORY; + goto endEntityValue; + } + *(pool->ptr)++ = buf[i]; + } + } + break; + case XML_TOK_PARTIAL: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + case XML_TOK_INVALID: + if (enc == encoding) + eventPtr = next; + result = XML_ERROR_INVALID_TOKEN; + goto endEntityValue; + default: + if (enc == encoding) + eventPtr = entityTextPtr; + result = XML_ERROR_UNEXPECTED_STATE; + goto endEntityValue; + } + entityTextPtr = next; + } +endEntityValue: +#ifdef XML_DTD + prologState.inEntityValue = oldInEntityValue; +#endif /* XML_DTD */ + return result; +} + +static void FASTCALL +normalizeLines(XML_Char *s) +{ + XML_Char *p; + for (;; s++) { + if (*s == XML_T('\0')) + return; + if (*s == 0xD) + break; + } + p = s; + do { + if (*s == 0xD) { + *p++ = 0xA; + if (*++s == 0xA) + s++; + } + else + *p++ = *s++; + } while (*s); + *p = XML_T('\0'); +} + +static int +reportProcessingInstruction(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + const XML_Char *target; + XML_Char *data; + const char *tem; + if (!processingInstructionHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + start += enc->minBytesPerChar * 2; + tem = start + XmlNameLength(enc, start); + target = poolStoreString(&tempPool, enc, start, tem); + if (!target) + return 0; + poolFinish(&tempPool); + data = poolStoreString(&tempPool, enc, + XmlSkipS(enc, tem), + end - enc->minBytesPerChar*2); + if (!data) + return 0; + normalizeLines(data); + processingInstructionHandler(handlerArg, target, data); + poolClear(&tempPool); + return 1; +} + +static int +reportComment(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + XML_Char *data; + if (!commentHandler) { + if (defaultHandler) + reportDefault(parser, enc, start, end); + return 1; + } + data = poolStoreString(&tempPool, + enc, + start + enc->minBytesPerChar * 4, + end - enc->minBytesPerChar * 3); + if (!data) + return 0; + normalizeLines(data); + commentHandler(handlerArg, data); + poolClear(&tempPool); + return 1; +} + +static void +reportDefault(XML_Parser parser, const ENCODING *enc, + const char *s, const char *end) +{ + if (MUST_CONVERT(enc, s)) { + const char **eventPP; + const char **eventEndPP; + if (enc == encoding) { + eventPP = &eventPtr; + eventEndPP = &eventEndPtr; + } + else { + eventPP = &(openInternalEntities->internalEventPtr); + eventEndPP = &(openInternalEntities->internalEventEndPtr); + } + do { + ICHAR *dataPtr = (ICHAR *)dataBuf; + XmlConvert(enc, &s, end, &dataPtr, (ICHAR *)dataBufEnd); + *eventEndPP = s; + defaultHandler(handlerArg, dataBuf, (int)(dataPtr - (ICHAR *)dataBuf)); + *eventPP = s; + } while (s != end); + } + else + defaultHandler(handlerArg, (XML_Char *)s, (int)((XML_Char *)end - (XML_Char *)s)); +} + + +static int +defineAttribute(ELEMENT_TYPE *type, ATTRIBUTE_ID *attId, XML_Bool isCdata, + XML_Bool isId, const XML_Char *value, XML_Parser parser) +{ + DEFAULT_ATTRIBUTE *att; + if (value || isId) { + /* The handling of default attributes gets messed up if we have + a default which duplicates a non-default. */ + int i; + for (i = 0; i < type->nDefaultAtts; i++) + if (attId == type->defaultAtts[i].id) + return 1; + if (isId && !type->idAtt && !attId->xmlns) + type->idAtt = attId; + } + if (type->nDefaultAtts == type->allocDefaultAtts) { + if (type->allocDefaultAtts == 0) { + type->allocDefaultAtts = 8; + type->defaultAtts = (DEFAULT_ATTRIBUTE *)MALLOC(type->allocDefaultAtts + * sizeof(DEFAULT_ATTRIBUTE)); + if (!type->defaultAtts) + return 0; + } + else { + DEFAULT_ATTRIBUTE *temp; + int count = type->allocDefaultAtts * 2; + temp = (DEFAULT_ATTRIBUTE *) + REALLOC(type->defaultAtts, (count * sizeof(DEFAULT_ATTRIBUTE))); + if (temp == NULL) + return 0; + type->allocDefaultAtts = count; + type->defaultAtts = temp; + } + } + att = type->defaultAtts + type->nDefaultAtts; + att->id = attId; + att->value = value; + att->isCdata = isCdata; + if (!isCdata) + attId->maybeTokenized = XML_TRUE; + type->nDefaultAtts += 1; + return 1; +} + +static int +setElementTypePrefix(XML_Parser parser, ELEMENT_TYPE *elementType) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name; + for (name = elementType->name; *name; name++) { + if (*name == XML_T(ASCII_COLON)) { + PREFIX *prefix; + const XML_Char *s; + for (s = elementType->name; s != name; s++) { + if (!poolAppendChar(&dtd->pool, *s)) + return 0; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return 0; + prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (!prefix) + return 0; + if (prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + elementType->prefix = prefix; + + } + } + return 1; +} + +static ATTRIBUTE_ID * +getAttributeId(XML_Parser parser, const ENCODING *enc, + const char *start, const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + ATTRIBUTE_ID *id; + const XML_Char *name; + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + name = poolStoreString(&dtd->pool, enc, start, end); + if (!name) + return NULL; + /* skip quotation mark - its storage will be re-used (like in name[-1]) */ + ++name; + id = (ATTRIBUTE_ID *)lookup(parser, &dtd->attributeIds, name, sizeof(ATTRIBUTE_ID)); + if (!id) + return NULL; + if (id->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!ns) + ; + else if (name[0] == XML_T(ASCII_x) + && name[1] == XML_T(ASCII_m) + && name[2] == XML_T(ASCII_l) + && name[3] == XML_T(ASCII_n) + && name[4] == XML_T(ASCII_s) + && (name[5] == XML_T('\0') || name[5] == XML_T(ASCII_COLON))) { + if (name[5] == XML_T('\0')) + id->prefix = &dtd->defaultPrefix; + else + id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, name + 6, sizeof(PREFIX)); + id->xmlns = XML_TRUE; + } + else { + int i; + for (i = 0; name[i]; i++) { + /* attributes without prefix are *not* in the default namespace */ + if (name[i] == XML_T(ASCII_COLON)) { + int j; + for (j = 0; j < i; j++) { + if (!poolAppendChar(&dtd->pool, name[j])) + return NULL; + } + if (!poolAppendChar(&dtd->pool, XML_T('\0'))) + return NULL; + id->prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&dtd->pool), + sizeof(PREFIX)); + if (id->prefix->name == poolStart(&dtd->pool)) + poolFinish(&dtd->pool); + else + poolDiscard(&dtd->pool); + break; + } + } + } + } + return id; +} + +#define CONTEXT_SEP XML_T(ASCII_FF) + +static const XML_Char * +getContext(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + HASH_TABLE_ITER iter; + XML_Bool needSep = XML_FALSE; + + if (dtd->defaultPrefix.binding) { + int i; + int len; + if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) + return NULL; + len = dtd->defaultPrefix.binding->uriLen; + if (namespaceSeparator) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, dtd->defaultPrefix.binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + hashTableIterInit(&iter, &(dtd->prefixes)); + for (;;) { + int i; + int len; + const XML_Char *s; + PREFIX *prefix = (PREFIX *)hashTableIterNext(&iter); + if (!prefix) + break; + if (!prefix->binding) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = prefix->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return NULL; + if (!poolAppendChar(&tempPool, XML_T(ASCII_EQUALS))) + return NULL; + len = prefix->binding->uriLen; + if (namespaceSeparator) + len--; + for (i = 0; i < len; i++) + if (!poolAppendChar(&tempPool, prefix->binding->uri[i])) + return NULL; + needSep = XML_TRUE; + } + + + hashTableIterInit(&iter, &(dtd->generalEntities)); + for (;;) { + const XML_Char *s; + ENTITY *e = (ENTITY *)hashTableIterNext(&iter); + if (!e) + break; + if (!e->open) + continue; + if (needSep && !poolAppendChar(&tempPool, CONTEXT_SEP)) + return NULL; + for (s = e->name; *s; s++) + if (!poolAppendChar(&tempPool, *s)) + return 0; + needSep = XML_TRUE; + } + + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return NULL; + return tempPool.start; +} + +static XML_Bool +setContext(XML_Parser parser, const XML_Char *context) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *s = context; + + while (*context != XML_T('\0')) { + if (*s == CONTEXT_SEP || *s == XML_T('\0')) { + ENTITY *e; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + e = (ENTITY *)lookup(parser, &dtd->generalEntities, poolStart(&tempPool), 0); + if (e) + e->open = XML_TRUE; + if (*s != XML_T('\0')) + s++; + context = s; + poolDiscard(&tempPool); + } + else if (*s == XML_T(ASCII_EQUALS)) { + PREFIX *prefix; + if (poolLength(&tempPool) == 0) + prefix = &dtd->defaultPrefix; + else { + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + prefix = (PREFIX *)lookup(parser, &dtd->prefixes, poolStart(&tempPool), + sizeof(PREFIX)); + if (!prefix) + return XML_FALSE; + if (prefix->name == poolStart(&tempPool)) { + prefix->name = poolCopyString(&dtd->pool, prefix->name); + if (!prefix->name) + return XML_FALSE; + } + poolDiscard(&tempPool); + } + for (context = s + 1; + *context != CONTEXT_SEP && *context != XML_T('\0'); + context++) + if (!poolAppendChar(&tempPool, *context)) + return XML_FALSE; + if (!poolAppendChar(&tempPool, XML_T('\0'))) + return XML_FALSE; + if (addBinding(parser, prefix, NULL, poolStart(&tempPool), + &inheritedBindings) != XML_ERROR_NONE) + return XML_FALSE; + poolDiscard(&tempPool); + if (*context != XML_T('\0')) + ++context; + s = context; + } + else { + if (!poolAppendChar(&tempPool, *s)) + return XML_FALSE; + s++; + } + } + return XML_TRUE; +} + +static void FASTCALL +normalizePublicId(XML_Char *publicId) +{ + XML_Char *p = publicId; + XML_Char *s; + for (s = publicId; *s; s++) { + switch (*s) { + case 0x20: + case 0xD: + case 0xA: + if (p != publicId && p[-1] != 0x20) + *p++ = 0x20; + break; + default: + *p++ = *s; + } + } + if (p != publicId && p[-1] == 0x20) + --p; + *p = XML_T('\0'); +} + +static DTD * +dtdCreate(const XML_Memory_Handling_Suite *ms) +{ + DTD *p = (DTD *)ms->malloc_fcn(sizeof(DTD)); + if (p == NULL) + return p; + poolInit(&(p->pool), ms); + poolInit(&(p->entityValuePool), ms); + hashTableInit(&(p->generalEntities), ms); + hashTableInit(&(p->elementTypes), ms); + hashTableInit(&(p->attributeIds), ms); + hashTableInit(&(p->prefixes), ms); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableInit(&(p->paramEntities), ms); +#endif /* XML_DTD */ + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + p->scaffIndex = NULL; + p->scaffold = NULL; + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; + return p; +} + +static void +dtdReset(DTD *p, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableClear(&(p->generalEntities)); +#ifdef XML_DTD + p->paramEntityRead = XML_FALSE; + hashTableClear(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableClear(&(p->elementTypes)); + hashTableClear(&(p->attributeIds)); + hashTableClear(&(p->prefixes)); + poolClear(&(p->pool)); + poolClear(&(p->entityValuePool)); + p->defaultPrefix.name = NULL; + p->defaultPrefix.binding = NULL; + + p->in_eldecl = XML_FALSE; + + ms->free_fcn(p->scaffIndex); + p->scaffIndex = NULL; + ms->free_fcn(p->scaffold); + p->scaffold = NULL; + + p->scaffLevel = 0; + p->scaffSize = 0; + p->scaffCount = 0; + p->contentStringLen = 0; + + p->keepProcessing = XML_TRUE; + p->hasParamEntityRefs = XML_FALSE; + p->standalone = XML_FALSE; +} + +static void +dtdDestroy(DTD *p, XML_Bool isDocEntity, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + hashTableIterInit(&iter, &(p->elementTypes)); + for (;;) { + ELEMENT_TYPE *e = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!e) + break; + if (e->allocDefaultAtts != 0) + ms->free_fcn(e->defaultAtts); + } + hashTableDestroy(&(p->generalEntities)); +#ifdef XML_DTD + hashTableDestroy(&(p->paramEntities)); +#endif /* XML_DTD */ + hashTableDestroy(&(p->elementTypes)); + hashTableDestroy(&(p->attributeIds)); + hashTableDestroy(&(p->prefixes)); + poolDestroy(&(p->pool)); + poolDestroy(&(p->entityValuePool)); + if (isDocEntity) { + ms->free_fcn(p->scaffIndex); + ms->free_fcn(p->scaffold); + } + ms->free_fcn(p); +} + +/* Do a deep copy of the DTD. Return 0 for out of memory, non-zero otherwise. + The new DTD has already been initialized. +*/ +static int +dtdCopy(XML_Parser oldParser, DTD *newDtd, const DTD *oldDtd, const XML_Memory_Handling_Suite *ms) +{ + HASH_TABLE_ITER iter; + + /* Copy the prefix table. */ + + hashTableIterInit(&iter, &(oldDtd->prefixes)); + for (;;) { + const XML_Char *name; + const PREFIX *oldP = (PREFIX *)hashTableIterNext(&iter); + if (!oldP) + break; + name = poolCopyString(&(newDtd->pool), oldP->name); + if (!name) + return 0; + if (!lookup(oldParser, &(newDtd->prefixes), name, sizeof(PREFIX))) + return 0; + } + + hashTableIterInit(&iter, &(oldDtd->attributeIds)); + + /* Copy the attribute id table. */ + + for (;;) { + ATTRIBUTE_ID *newA; + const XML_Char *name; + const ATTRIBUTE_ID *oldA = (ATTRIBUTE_ID *)hashTableIterNext(&iter); + + if (!oldA) + break; + /* Remember to allocate the scratch byte before the name. */ + if (!poolAppendChar(&(newDtd->pool), XML_T('\0'))) + return 0; + name = poolCopyString(&(newDtd->pool), oldA->name); + if (!name) + return 0; + ++name; + newA = (ATTRIBUTE_ID *)lookup(oldParser, &(newDtd->attributeIds), name, + sizeof(ATTRIBUTE_ID)); + if (!newA) + return 0; + newA->maybeTokenized = oldA->maybeTokenized; + if (oldA->prefix) { + newA->xmlns = oldA->xmlns; + if (oldA->prefix == &oldDtd->defaultPrefix) + newA->prefix = &newDtd->defaultPrefix; + else + newA->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), + oldA->prefix->name, 0); + } + } + + /* Copy the element type table. */ + + hashTableIterInit(&iter, &(oldDtd->elementTypes)); + + for (;;) { + int i; + ELEMENT_TYPE *newE; + const XML_Char *name; + const ELEMENT_TYPE *oldE = (ELEMENT_TYPE *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(&(newDtd->pool), oldE->name); + if (!name) + return 0; + newE = (ELEMENT_TYPE *)lookup(oldParser, &(newDtd->elementTypes), name, + sizeof(ELEMENT_TYPE)); + if (!newE) + return 0; + if (oldE->nDefaultAtts) { + newE->defaultAtts = (DEFAULT_ATTRIBUTE *) + ms->malloc_fcn(oldE->nDefaultAtts * sizeof(DEFAULT_ATTRIBUTE)); + if (!newE->defaultAtts) { + ms->free_fcn(newE); + return 0; + } + } + if (oldE->idAtt) + newE->idAtt = (ATTRIBUTE_ID *) + lookup(oldParser, &(newDtd->attributeIds), oldE->idAtt->name, 0); + newE->allocDefaultAtts = newE->nDefaultAtts = oldE->nDefaultAtts; + if (oldE->prefix) + newE->prefix = (PREFIX *)lookup(oldParser, &(newDtd->prefixes), + oldE->prefix->name, 0); + for (i = 0; i < newE->nDefaultAtts; i++) { + newE->defaultAtts[i].id = (ATTRIBUTE_ID *) + lookup(oldParser, &(newDtd->attributeIds), oldE->defaultAtts[i].id->name, 0); + newE->defaultAtts[i].isCdata = oldE->defaultAtts[i].isCdata; + if (oldE->defaultAtts[i].value) { + newE->defaultAtts[i].value + = poolCopyString(&(newDtd->pool), oldE->defaultAtts[i].value); + if (!newE->defaultAtts[i].value) + return 0; + } + else + newE->defaultAtts[i].value = NULL; + } + } + + /* Copy the entity tables. */ + if (!copyEntityTable(oldParser, + &(newDtd->generalEntities), + &(newDtd->pool), + &(oldDtd->generalEntities))) + return 0; + +#ifdef XML_DTD + if (!copyEntityTable(oldParser, + &(newDtd->paramEntities), + &(newDtd->pool), + &(oldDtd->paramEntities))) + return 0; + newDtd->paramEntityRead = oldDtd->paramEntityRead; +#endif /* XML_DTD */ + + newDtd->keepProcessing = oldDtd->keepProcessing; + newDtd->hasParamEntityRefs = oldDtd->hasParamEntityRefs; + newDtd->standalone = oldDtd->standalone; + + /* Don't want deep copying for scaffolding */ + newDtd->in_eldecl = oldDtd->in_eldecl; + newDtd->scaffold = oldDtd->scaffold; + newDtd->contentStringLen = oldDtd->contentStringLen; + newDtd->scaffSize = oldDtd->scaffSize; + newDtd->scaffLevel = oldDtd->scaffLevel; + newDtd->scaffIndex = oldDtd->scaffIndex; + + return 1; +} /* End dtdCopy */ + +static int +copyEntityTable(XML_Parser oldParser, + HASH_TABLE *newTable, + STRING_POOL *newPool, + const HASH_TABLE *oldTable) +{ + HASH_TABLE_ITER iter; + const XML_Char *cachedOldBase = NULL; + const XML_Char *cachedNewBase = NULL; + + hashTableIterInit(&iter, oldTable); + + for (;;) { + ENTITY *newE; + const XML_Char *name; + const ENTITY *oldE = (ENTITY *)hashTableIterNext(&iter); + if (!oldE) + break; + name = poolCopyString(newPool, oldE->name); + if (!name) + return 0; + newE = (ENTITY *)lookup(oldParser, newTable, name, sizeof(ENTITY)); + if (!newE) + return 0; + if (oldE->systemId) { + const XML_Char *tem = poolCopyString(newPool, oldE->systemId); + if (!tem) + return 0; + newE->systemId = tem; + if (oldE->base) { + if (oldE->base == cachedOldBase) + newE->base = cachedNewBase; + else { + cachedOldBase = oldE->base; + tem = poolCopyString(newPool, cachedOldBase); + if (!tem) + return 0; + cachedNewBase = newE->base = tem; + } + } + if (oldE->publicId) { + tem = poolCopyString(newPool, oldE->publicId); + if (!tem) + return 0; + newE->publicId = tem; + } + } + else { + const XML_Char *tem = poolCopyStringN(newPool, oldE->textPtr, + oldE->textLen); + if (!tem) + return 0; + newE->textPtr = tem; + newE->textLen = oldE->textLen; + } + if (oldE->notation) { + const XML_Char *tem = poolCopyString(newPool, oldE->notation); + if (!tem) + return 0; + newE->notation = tem; + } + newE->is_param = oldE->is_param; + newE->is_internal = oldE->is_internal; + } + return 1; +} + +#define INIT_POWER 6 + +static XML_Bool FASTCALL +keyeq(KEY s1, KEY s2) +{ + for (; *s1 == *s2; s1++, s2++) + if (*s1 == 0) + return XML_TRUE; + return XML_FALSE; +} + +static unsigned long FASTCALL +hash(XML_Parser parser, KEY s) +{ + unsigned long h = hash_secret_salt; + while (*s) + h = CHAR_HASH(h, *s++); + return h; +} + +static NAMED * +lookup(XML_Parser parser, HASH_TABLE *table, KEY name, size_t createSize) +{ + size_t i; + if (table->size == 0) { + size_t tsize; + if (!createSize) + return NULL; + table->power = INIT_POWER; + /* table->size is a power of 2 */ + table->size = (size_t)1 << INIT_POWER; + tsize = table->size * sizeof(NAMED *); + table->v = (NAMED **)table->mem->malloc_fcn(tsize); + if (!table->v) { + table->size = 0; + return NULL; + } + memset(table->v, 0, tsize); + i = hash(parser, name) & ((unsigned long)table->size - 1); + } + else { + unsigned long h = hash(parser, name); + unsigned long mask = (unsigned long)table->size - 1; + unsigned char step = 0; + i = h & mask; + while (table->v[i]) { + if (keyeq(name, table->v[i]->name)) + return table->v[i]; + if (!step) + step = PROBE_STEP(h, mask, table->power); + i < step ? (i += table->size - step) : (i -= step); + } + if (!createSize) + return NULL; + + /* check for overflow (table is half full) */ + if (table->used >> (table->power - 1)) { + unsigned char newPower = table->power + 1; + size_t newSize = (size_t)1 << newPower; + unsigned long newMask = (unsigned long)newSize - 1; + size_t tsize = newSize * sizeof(NAMED *); + NAMED **newV = (NAMED **)table->mem->malloc_fcn(tsize); + if (!newV) + return NULL; + memset(newV, 0, tsize); + for (i = 0; i < table->size; i++) + if (table->v[i]) { + unsigned long newHash = hash(parser, table->v[i]->name); + size_t j = newHash & newMask; + step = 0; + while (newV[j]) { + if (!step) + step = PROBE_STEP(newHash, newMask, newPower); + j < step ? (j += newSize - step) : (j -= step); + } + newV[j] = table->v[i]; + } + table->mem->free_fcn(table->v); + table->v = newV; + table->power = newPower; + table->size = newSize; + i = h & newMask; + step = 0; + while (table->v[i]) { + if (!step) + step = PROBE_STEP(h, newMask, newPower); + i < step ? (i += newSize - step) : (i -= step); + } + } + } + table->v[i] = (NAMED *)table->mem->malloc_fcn(createSize); + if (!table->v[i]) + return NULL; + memset(table->v[i], 0, createSize); + table->v[i]->name = name; + (table->used)++; + return table->v[i]; +} + +static void FASTCALL +hashTableClear(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) { + table->mem->free_fcn(table->v[i]); + table->v[i] = NULL; + } + table->used = 0; +} + +static void FASTCALL +hashTableDestroy(HASH_TABLE *table) +{ + size_t i; + for (i = 0; i < table->size; i++) + table->mem->free_fcn(table->v[i]); + table->mem->free_fcn(table->v); +} + +static void FASTCALL +hashTableInit(HASH_TABLE *p, const XML_Memory_Handling_Suite *ms) +{ + p->power = 0; + p->size = 0; + p->used = 0; + p->v = NULL; + p->mem = ms; +} + +static void FASTCALL +hashTableIterInit(HASH_TABLE_ITER *iter, const HASH_TABLE *table) +{ + iter->p = table->v; + iter->end = iter->p + table->size; +} + +static NAMED * FASTCALL +hashTableIterNext(HASH_TABLE_ITER *iter) +{ + while (iter->p != iter->end) { + NAMED *tem = *(iter->p)++; + if (tem) + return tem; + } + return NULL; +} + +static void FASTCALL +poolInit(STRING_POOL *pool, const XML_Memory_Handling_Suite *ms) +{ + pool->blocks = NULL; + pool->freeBlocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; + pool->mem = ms; +} + +static void FASTCALL +poolClear(STRING_POOL *pool) +{ + if (!pool->freeBlocks) + pool->freeBlocks = pool->blocks; + else { + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + p->next = pool->freeBlocks; + pool->freeBlocks = p; + p = tem; + } + } + pool->blocks = NULL; + pool->start = NULL; + pool->ptr = NULL; + pool->end = NULL; +} + +static void FASTCALL +poolDestroy(STRING_POOL *pool) +{ + BLOCK *p = pool->blocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } + p = pool->freeBlocks; + while (p) { + BLOCK *tem = p->next; + pool->mem->free_fcn(p); + p = tem; + } +} + +static XML_Char * +poolAppend(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (;;) { + XmlConvert(enc, &ptr, end, (ICHAR **)&(pool->ptr), (ICHAR *)pool->end); + if (ptr == end) + break; + if (!poolGrow(pool)) + return NULL; + } + return pool->start; +} + +static const XML_Char * FASTCALL +poolCopyString(STRING_POOL *pool, const XML_Char *s) +{ + do { + if (!poolAppendChar(pool, *s)) + return NULL; + } while (*s++); + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * +poolCopyStringN(STRING_POOL *pool, const XML_Char *s, int n) +{ + if (!pool->ptr && !poolGrow(pool)) + return NULL; + for (; n > 0; --n, s++) { + if (!poolAppendChar(pool, *s)) + return NULL; + } + s = pool->start; + poolFinish(pool); + return s; +} + +static const XML_Char * FASTCALL +poolAppendString(STRING_POOL *pool, const XML_Char *s) +{ + while (*s) { + if (!poolAppendChar(pool, *s)) + return NULL; + s++; + } + return pool->start; +} + +static XML_Char * +poolStoreString(STRING_POOL *pool, const ENCODING *enc, + const char *ptr, const char *end) +{ + if (!poolAppend(pool, enc, ptr, end)) + return NULL; + if (pool->ptr == pool->end && !poolGrow(pool)) + return NULL; + *(pool->ptr)++ = 0; + return pool->start; +} + +static XML_Bool FASTCALL +poolGrow(STRING_POOL *pool) +{ + if (pool->freeBlocks) { + if (pool->start == 0) { + pool->blocks = pool->freeBlocks; + pool->freeBlocks = pool->freeBlocks->next; + pool->blocks->next = NULL; + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + pool->ptr = pool->start; + return XML_TRUE; + } + if (pool->end - pool->start < pool->freeBlocks->size) { + BLOCK *tem = pool->freeBlocks->next; + pool->freeBlocks->next = pool->blocks; + pool->blocks = pool->freeBlocks; + pool->freeBlocks = tem; + memcpy(pool->blocks->s, pool->start, + (pool->end - pool->start) * sizeof(XML_Char)); + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + pool->blocks->size; + return XML_TRUE; + } + } + if (pool->blocks && pool->start == pool->blocks->s) { + int blockSize = (int)(pool->end - pool->start)*2; + BLOCK *temp = (BLOCK *) + pool->mem->realloc_fcn(pool->blocks, + (offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char))); + if (temp == NULL) + return XML_FALSE; + pool->blocks = temp; + pool->blocks->size = blockSize; + pool->ptr = pool->blocks->s + (pool->ptr - pool->start); + pool->start = pool->blocks->s; + pool->end = pool->start + blockSize; + } + else { + BLOCK *tem; + int blockSize = (int)(pool->end - pool->start); + if (blockSize < INIT_BLOCK_SIZE) + blockSize = INIT_BLOCK_SIZE; + else + blockSize *= 2; + tem = (BLOCK *)pool->mem->malloc_fcn(offsetof(BLOCK, s) + + blockSize * sizeof(XML_Char)); + if (!tem) + return XML_FALSE; + tem->size = blockSize; + tem->next = pool->blocks; + pool->blocks = tem; + if (pool->ptr != pool->start) + memcpy(tem->s, pool->start, + (pool->ptr - pool->start) * sizeof(XML_Char)); + pool->ptr = tem->s + (pool->ptr - pool->start); + pool->start = tem->s; + pool->end = tem->s + blockSize; + } + return XML_TRUE; +} + +static int FASTCALL +nextScaffoldPart(XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + CONTENT_SCAFFOLD * me; + int next; + + if (!dtd->scaffIndex) { + dtd->scaffIndex = (int *)MALLOC(groupSize * sizeof(int)); + if (!dtd->scaffIndex) + return -1; + dtd->scaffIndex[0] = 0; + } + + if (dtd->scaffCount >= dtd->scaffSize) { + CONTENT_SCAFFOLD *temp; + if (dtd->scaffold) { + temp = (CONTENT_SCAFFOLD *) + REALLOC(dtd->scaffold, dtd->scaffSize * 2 * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize *= 2; + } + else { + temp = (CONTENT_SCAFFOLD *)MALLOC(INIT_SCAFFOLD_ELEMENTS + * sizeof(CONTENT_SCAFFOLD)); + if (temp == NULL) + return -1; + dtd->scaffSize = INIT_SCAFFOLD_ELEMENTS; + } + dtd->scaffold = temp; + } + next = dtd->scaffCount++; + me = &dtd->scaffold[next]; + if (dtd->scaffLevel) { + CONTENT_SCAFFOLD *parent = &dtd->scaffold[dtd->scaffIndex[dtd->scaffLevel-1]]; + if (parent->lastchild) { + dtd->scaffold[parent->lastchild].nextsib = next; + } + if (!parent->childcnt) + parent->firstchild = next; + parent->lastchild = next; + parent->childcnt++; + } + me->firstchild = me->lastchild = me->childcnt = me->nextsib = 0; + return next; +} + +static void +build_node(XML_Parser parser, + int src_node, + XML_Content *dest, + XML_Content **contpos, + XML_Char **strpos) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + dest->type = dtd->scaffold[src_node].type; + dest->quant = dtd->scaffold[src_node].quant; + if (dest->type == XML_CTYPE_NAME) { + const XML_Char *src; + dest->name = *strpos; + src = dtd->scaffold[src_node].name; + for (;;) { + *(*strpos)++ = *src; + if (!*src) + break; + src++; + } + dest->numchildren = 0; + dest->children = NULL; + } + else { + unsigned int i; + int cn; + dest->numchildren = dtd->scaffold[src_node].childcnt; + dest->children = *contpos; + *contpos += dest->numchildren; + for (i = 0, cn = dtd->scaffold[src_node].firstchild; + i < dest->numchildren; + i++, cn = dtd->scaffold[cn].nextsib) { + build_node(parser, cn, &(dest->children[i]), contpos, strpos); + } + dest->name = NULL; + } +} + +static XML_Content * +build_model (XML_Parser parser) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + XML_Content *ret; + XML_Content *cpos; + XML_Char * str; + int allocsize = (dtd->scaffCount * sizeof(XML_Content) + + (dtd->contentStringLen * sizeof(XML_Char))); + + ret = (XML_Content *)MALLOC(allocsize); + if (!ret) + return NULL; + + str = (XML_Char *) (&ret[dtd->scaffCount]); + cpos = &ret[1]; + + build_node(parser, 0, ret, &cpos, &str); + return ret; +} + +static ELEMENT_TYPE * +getElementType(XML_Parser parser, + const ENCODING *enc, + const char *ptr, + const char *end) +{ + DTD * const dtd = _dtd; /* save one level of indirection */ + const XML_Char *name = poolStoreString(&dtd->pool, enc, ptr, end); + ELEMENT_TYPE *ret; + + if (!name) + return NULL; + ret = (ELEMENT_TYPE *) lookup(parser, &dtd->elementTypes, name, sizeof(ELEMENT_TYPE)); + if (!ret) + return NULL; + if (ret->name != name) + poolDiscard(&dtd->pool); + else { + poolFinish(&dtd->pool); + if (!setElementTypePrefix(parser, ret)) + return NULL; + } + return ret; +} diff --git a/source/lib/expat_lib/xmlrole.c b/source/lib/expat_lib/xmlrole.c new file mode 100644 index 0000000..44772e2 --- /dev/null +++ b/source/lib/expat_lib/xmlrole.c @@ -0,0 +1,1336 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include + +#ifdef COMPILED_FROM_DSP +#include "winconfig.h" +#elif defined(MACOS_CLASSIC) +#include "macconfig.h" +#elif defined(__amigaos__) +#include "amigaconfig.h" +#elif defined(__WATCOMC__) +#include "watcomconfig.h" +#else +#ifdef HAVE_EXPAT_CONFIG_H +#include +#endif +#endif /* ndef COMPILED_FROM_DSP */ + +#include "expat_external.h" +#include "internal.h" +#include "xmlrole.h" +#include "ascii.h" + +/* Doesn't check: + + that ,| are not mixed in a model group + content of literals + +*/ + +static const char KW_ANY[] = { + ASCII_A, ASCII_N, ASCII_Y, '\0' }; +static const char KW_ATTLIST[] = { + ASCII_A, ASCII_T, ASCII_T, ASCII_L, ASCII_I, ASCII_S, ASCII_T, '\0' }; +static const char KW_CDATA[] = { + ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_DOCTYPE[] = { + ASCII_D, ASCII_O, ASCII_C, ASCII_T, ASCII_Y, ASCII_P, ASCII_E, '\0' }; +static const char KW_ELEMENT[] = { + ASCII_E, ASCII_L, ASCII_E, ASCII_M, ASCII_E, ASCII_N, ASCII_T, '\0' }; +static const char KW_EMPTY[] = { + ASCII_E, ASCII_M, ASCII_P, ASCII_T, ASCII_Y, '\0' }; +static const char KW_ENTITIES[] = { + ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_I, ASCII_E, ASCII_S, + '\0' }; +static const char KW_ENTITY[] = { + ASCII_E, ASCII_N, ASCII_T, ASCII_I, ASCII_T, ASCII_Y, '\0' }; +static const char KW_FIXED[] = { + ASCII_F, ASCII_I, ASCII_X, ASCII_E, ASCII_D, '\0' }; +static const char KW_ID[] = { + ASCII_I, ASCII_D, '\0' }; +static const char KW_IDREF[] = { + ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, '\0' }; +static const char KW_IDREFS[] = { + ASCII_I, ASCII_D, ASCII_R, ASCII_E, ASCII_F, ASCII_S, '\0' }; +#ifdef XML_DTD +static const char KW_IGNORE[] = { + ASCII_I, ASCII_G, ASCII_N, ASCII_O, ASCII_R, ASCII_E, '\0' }; +#endif +static const char KW_IMPLIED[] = { + ASCII_I, ASCII_M, ASCII_P, ASCII_L, ASCII_I, ASCII_E, ASCII_D, '\0' }; +#ifdef XML_DTD +static const char KW_INCLUDE[] = { + ASCII_I, ASCII_N, ASCII_C, ASCII_L, ASCII_U, ASCII_D, ASCII_E, '\0' }; +#endif +static const char KW_NDATA[] = { + ASCII_N, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_NMTOKEN[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, '\0' }; +static const char KW_NMTOKENS[] = { + ASCII_N, ASCII_M, ASCII_T, ASCII_O, ASCII_K, ASCII_E, ASCII_N, ASCII_S, + '\0' }; +static const char KW_NOTATION[] = + { ASCII_N, ASCII_O, ASCII_T, ASCII_A, ASCII_T, ASCII_I, ASCII_O, ASCII_N, + '\0' }; +static const char KW_PCDATA[] = { + ASCII_P, ASCII_C, ASCII_D, ASCII_A, ASCII_T, ASCII_A, '\0' }; +static const char KW_PUBLIC[] = { + ASCII_P, ASCII_U, ASCII_B, ASCII_L, ASCII_I, ASCII_C, '\0' }; +static const char KW_REQUIRED[] = { + ASCII_R, ASCII_E, ASCII_Q, ASCII_U, ASCII_I, ASCII_R, ASCII_E, ASCII_D, + '\0' }; +static const char KW_SYSTEM[] = { + ASCII_S, ASCII_Y, ASCII_S, ASCII_T, ASCII_E, ASCII_M, '\0' }; + +#ifndef MIN_BYTES_PER_CHAR +#define MIN_BYTES_PER_CHAR(enc) ((enc)->minBytesPerChar) +#endif + +#ifdef XML_DTD +#define setTopLevel(state) \ + ((state)->handler = ((state)->documentEntity \ + ? internalSubset \ + : externalSubset1)) +#else /* not XML_DTD */ +#define setTopLevel(state) ((state)->handler = internalSubset) +#endif /* not XML_DTD */ + +typedef int PTRCALL PROLOG_HANDLER(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + +static PROLOG_HANDLER + prolog0, prolog1, prolog2, + doctype0, doctype1, doctype2, doctype3, doctype4, doctype5, + internalSubset, + entity0, entity1, entity2, entity3, entity4, entity5, entity6, + entity7, entity8, entity9, entity10, + notation0, notation1, notation2, notation3, notation4, + attlist0, attlist1, attlist2, attlist3, attlist4, attlist5, attlist6, + attlist7, attlist8, attlist9, + element0, element1, element2, element3, element4, element5, element6, + element7, +#ifdef XML_DTD + externalSubset0, externalSubset1, + condSect0, condSect1, condSect2, +#endif /* XML_DTD */ + declClose, + error; + +static int FASTCALL common(PROLOG_STATE *state, int tok); + +static int PTRCALL +prolog0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + state->handler = prolog1; + return XML_ROLE_NONE; + case XML_TOK_XML_DECL: + state->handler = prolog1; + return XML_ROLE_XML_DECL; + case XML_TOK_PI: + state->handler = prolog1; + return XML_ROLE_PI; + case XML_TOK_COMMENT: + state->handler = prolog1; + return XML_ROLE_COMMENT; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +prolog1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_BOM: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (!XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_DOCTYPE)) + break; + state->handler = doctype0; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +prolog2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_INSTANCE_START: + state->handler = error; + return XML_ROLE_INSTANCE_START; + } + return common(state, tok); +} + +static int PTRCALL +doctype0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = doctype1; + return XML_ROLE_DOCTYPE_NAME; + } + return common(state, tok); +} + +static int PTRCALL +doctype1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = doctype3; + return XML_ROLE_DOCTYPE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = doctype2; + return XML_ROLE_DOCTYPE_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +doctype2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype3; + return XML_ROLE_DOCTYPE_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +doctype3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_LITERAL: + state->handler = doctype4; + return XML_ROLE_DOCTYPE_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +doctype4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = internalSubset; + return XML_ROLE_DOCTYPE_INTERNAL_SUBSET; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static int PTRCALL +doctype5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_DECL_CLOSE: + state->handler = prolog2; + return XML_ROLE_DOCTYPE_CLOSE; + } + return common(state, tok); +} + +static int PTRCALL +internalSubset(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_DECL_OPEN: + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ENTITY)) { + state->handler = entity0; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ATTLIST)) { + state->handler = attlist0; + return XML_ROLE_ATTLIST_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_ELEMENT)) { + state->handler = element0; + return XML_ROLE_ELEMENT_NONE; + } + if (XmlNameMatchesAscii(enc, + ptr + 2 * MIN_BYTES_PER_CHAR(enc), + end, + KW_NOTATION)) { + state->handler = notation0; + return XML_ROLE_NOTATION_NONE; + } + break; + case XML_TOK_PI: + return XML_ROLE_PI; + case XML_TOK_COMMENT: + return XML_ROLE_COMMENT; + case XML_TOK_PARAM_ENTITY_REF: + return XML_ROLE_PARAM_ENTITY_REF; + case XML_TOK_CLOSE_BRACKET: + state->handler = doctype5; + return XML_ROLE_DOCTYPE_NONE; + case XML_TOK_NONE: + return XML_ROLE_NONE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static int PTRCALL +externalSubset0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + state->handler = externalSubset1; + if (tok == XML_TOK_XML_DECL) + return XML_ROLE_TEXT_DECL; + return externalSubset1(state, tok, ptr, end, enc); +} + +static int PTRCALL +externalSubset1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_COND_SECT_OPEN: + state->handler = condSect0; + return XML_ROLE_NONE; + case XML_TOK_COND_SECT_CLOSE: + if (state->includeLevel == 0) + break; + state->includeLevel -= 1; + return XML_ROLE_NONE; + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_CLOSE_BRACKET: + break; + case XML_TOK_NONE: + if (state->includeLevel) + break; + return XML_ROLE_NONE; + default: + return internalSubset(state, tok, ptr, end, enc); + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static int PTRCALL +entity0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_PERCENT: + state->handler = entity1; + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = entity2; + return XML_ROLE_GENERAL_ENTITY_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = entity7; + return XML_ROLE_PARAM_ENTITY_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity4; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity3; + return XML_ROLE_ENTITY_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +entity3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity4; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity5; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ENTITY_COMPLETE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_NDATA)) { + state->handler = entity6; + return XML_ROLE_ENTITY_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +entity6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_NOTATION_NAME; + } + return common(state, tok); +} + +static int PTRCALL +entity7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = entity9; + return XML_ROLE_ENTITY_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = entity8; + return XML_ROLE_ENTITY_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_ENTITY_NONE; + return XML_ROLE_ENTITY_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +entity8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity9; + return XML_ROLE_ENTITY_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_LITERAL: + state->handler = entity10; + return XML_ROLE_ENTITY_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +entity10(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ENTITY_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ENTITY_COMPLETE; + } + return common(state, tok); +} + +static int PTRCALL +notation0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_NAME: + state->handler = notation1; + return XML_ROLE_NOTATION_NAME; + } + return common(state, tok); +} + +static int PTRCALL +notation1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_SYSTEM)) { + state->handler = notation3; + return XML_ROLE_NOTATION_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_PUBLIC)) { + state->handler = notation2; + return XML_ROLE_NOTATION_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +notation2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = notation4; + return XML_ROLE_NOTATION_PUBLIC_ID; + } + return common(state, tok); +} + +static int PTRCALL +notation3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_NOTATION_NONE; + return XML_ROLE_NOTATION_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +notation4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NOTATION_NONE; + case XML_TOK_LITERAL: + state->handler = declClose; + state->role_none = XML_ROLE_NOTATION_NONE; + return XML_ROLE_NOTATION_SYSTEM_ID; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_NOTATION_NO_SYSTEM_ID; + } + return common(state, tok); +} + +static int PTRCALL +attlist0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist1; + return XML_ROLE_ATTLIST_ELEMENT_NAME; + } + return common(state, tok); +} + +static int PTRCALL +attlist1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist2; + return XML_ROLE_ATTRIBUTE_NAME; + } + return common(state, tok); +} + +static int PTRCALL +attlist2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + { + static const char * const types[] = { + KW_CDATA, + KW_ID, + KW_IDREF, + KW_IDREFS, + KW_ENTITY, + KW_ENTITIES, + KW_NMTOKEN, + KW_NMTOKENS, + }; + int i; + for (i = 0; i < (int)(sizeof(types)/sizeof(types[0])); i++) + if (XmlNameMatchesAscii(enc, ptr, end, types[i])) { + state->handler = attlist8; + return XML_ROLE_ATTRIBUTE_TYPE_CDATA + i; + } + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_NOTATION)) { + state->handler = attlist5; + return XML_ROLE_ATTLIST_NONE; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = attlist3; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NMTOKEN: + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = attlist4; + return XML_ROLE_ATTRIBUTE_ENUM_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OR: + state->handler = attlist3; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OPEN_PAREN: + state->handler = attlist6; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +static int PTRCALL +attlist6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_NAME: + state->handler = attlist7; + return XML_ROLE_ATTRIBUTE_NOTATION_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = attlist8; + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_OR: + state->handler = attlist6; + return XML_ROLE_ATTLIST_NONE; + } + return common(state, tok); +} + +/* default value */ +static int PTRCALL +attlist8(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_IMPLIED)) { + state->handler = attlist1; + return XML_ROLE_IMPLIED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_REQUIRED)) { + state->handler = attlist1; + return XML_ROLE_REQUIRED_ATTRIBUTE_VALUE; + } + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_FIXED)) { + state->handler = attlist9; + return XML_ROLE_ATTLIST_NONE; + } + break; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_DEFAULT_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +attlist9(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ATTLIST_NONE; + case XML_TOK_LITERAL: + state->handler = attlist1; + return XML_ROLE_FIXED_ATTRIBUTE_VALUE; + } + return common(state, tok); +} + +static int PTRCALL +element0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element1; + return XML_ROLE_ELEMENT_NAME; + } + return common(state, tok); +} + +static int PTRCALL +element1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_EMPTY)) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_CONTENT_EMPTY; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_ANY)) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_CONTENT_ANY; + } + break; + case XML_TOK_OPEN_PAREN: + state->handler = element2; + state->level = 1; + return XML_ROLE_GROUP_OPEN; + } + return common(state, tok); +} + +static int PTRCALL +element2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_POUND_NAME: + if (XmlNameMatchesAscii(enc, + ptr + MIN_BYTES_PER_CHAR(enc), + end, + KW_PCDATA)) { + state->handler = element3; + return XML_ROLE_CONTENT_PCDATA; + } + break; + case XML_TOK_OPEN_PAREN: + state->level = 2; + state->handler = element6; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static int PTRCALL +element3(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_ELEMENT_NONE; + } + return common(state, tok); +} + +static int PTRCALL +element4(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element5; + return XML_ROLE_CONTENT_ELEMENT; + } + return common(state, tok); +} + +static int PTRCALL +element5(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_OR: + state->handler = element4; + return XML_ROLE_ELEMENT_NONE; + } + return common(state, tok); +} + +static int PTRCALL +element6(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_OPEN_PAREN: + state->level += 1; + return XML_ROLE_GROUP_OPEN; + case XML_TOK_NAME: + case XML_TOK_PREFIXED_NAME: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT; + case XML_TOK_NAME_QUESTION: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_OPT; + case XML_TOK_NAME_ASTERISK: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_REP; + case XML_TOK_NAME_PLUS: + state->handler = element7; + return XML_ROLE_CONTENT_ELEMENT_PLUS; + } + return common(state, tok); +} + +static int PTRCALL +element7(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_ELEMENT_NONE; + case XML_TOK_CLOSE_PAREN: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE; + case XML_TOK_CLOSE_PAREN_ASTERISK: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_REP; + case XML_TOK_CLOSE_PAREN_QUESTION: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_OPT; + case XML_TOK_CLOSE_PAREN_PLUS: + state->level -= 1; + if (state->level == 0) { + state->handler = declClose; + state->role_none = XML_ROLE_ELEMENT_NONE; + } + return XML_ROLE_GROUP_CLOSE_PLUS; + case XML_TOK_COMMA: + state->handler = element6; + return XML_ROLE_GROUP_SEQUENCE; + case XML_TOK_OR: + state->handler = element6; + return XML_ROLE_GROUP_CHOICE; + } + return common(state, tok); +} + +#ifdef XML_DTD + +static int PTRCALL +condSect0(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_NAME: + if (XmlNameMatchesAscii(enc, ptr, end, KW_INCLUDE)) { + state->handler = condSect1; + return XML_ROLE_NONE; + } + if (XmlNameMatchesAscii(enc, ptr, end, KW_IGNORE)) { + state->handler = condSect2; + return XML_ROLE_NONE; + } + break; + } + return common(state, tok); +} + +static int PTRCALL +condSect1(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + state->includeLevel += 1; + return XML_ROLE_NONE; + } + return common(state, tok); +} + +static int PTRCALL +condSect2(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return XML_ROLE_NONE; + case XML_TOK_OPEN_BRACKET: + state->handler = externalSubset1; + return XML_ROLE_IGNORE_SECT; + } + return common(state, tok); +} + +#endif /* XML_DTD */ + +static int PTRCALL +declClose(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + switch (tok) { + case XML_TOK_PROLOG_S: + return state->role_none; + case XML_TOK_DECL_CLOSE: + setTopLevel(state); + return state->role_none; + } + return common(state, tok); +} + +static int PTRCALL +error(PROLOG_STATE *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc) +{ + return XML_ROLE_NONE; +} + +static int FASTCALL +common(PROLOG_STATE *state, int tok) +{ +#ifdef XML_DTD + if (!state->documentEntity && tok == XML_TOK_PARAM_ENTITY_REF) + return XML_ROLE_INNER_PARAM_ENTITY_REF; +#endif + state->handler = error; + return XML_ROLE_ERROR; +} + +void +XmlPrologStateInit(PROLOG_STATE *state) +{ + state->handler = prolog0; +#ifdef XML_DTD + state->documentEntity = 1; + state->includeLevel = 0; + state->inEntityValue = 0; +#endif /* XML_DTD */ +} + +#ifdef XML_DTD + +void +XmlPrologStateInitExternalEntity(PROLOG_STATE *state) +{ + state->handler = externalSubset0; + state->documentEntity = 0; + state->includeLevel = 0; +} + +#endif /* XML_DTD */ diff --git a/source/lib/expat_lib/xmlrole.h b/source/lib/expat_lib/xmlrole.h new file mode 100644 index 0000000..4dd9f06 --- /dev/null +++ b/source/lib/expat_lib/xmlrole.h @@ -0,0 +1,114 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlRole_INCLUDED +#define XmlRole_INCLUDED 1 + +#ifdef __VMS +/* 0 1 2 3 0 1 2 3 + 1234567890123456789012345678901 1234567890123456789012345678901 */ +#define XmlPrologStateInitExternalEntity XmlPrologStateInitExternalEnt +#endif + +#include "xmltok.h" + +#ifdef __cplusplus +extern "C" { +#endif + +enum { + XML_ROLE_ERROR = -1, + XML_ROLE_NONE = 0, + XML_ROLE_XML_DECL, + XML_ROLE_INSTANCE_START, + XML_ROLE_DOCTYPE_NONE, + XML_ROLE_DOCTYPE_NAME, + XML_ROLE_DOCTYPE_SYSTEM_ID, + XML_ROLE_DOCTYPE_PUBLIC_ID, + XML_ROLE_DOCTYPE_INTERNAL_SUBSET, + XML_ROLE_DOCTYPE_CLOSE, + XML_ROLE_GENERAL_ENTITY_NAME, + XML_ROLE_PARAM_ENTITY_NAME, + XML_ROLE_ENTITY_NONE, + XML_ROLE_ENTITY_VALUE, + XML_ROLE_ENTITY_SYSTEM_ID, + XML_ROLE_ENTITY_PUBLIC_ID, + XML_ROLE_ENTITY_COMPLETE, + XML_ROLE_ENTITY_NOTATION_NAME, + XML_ROLE_NOTATION_NONE, + XML_ROLE_NOTATION_NAME, + XML_ROLE_NOTATION_SYSTEM_ID, + XML_ROLE_NOTATION_NO_SYSTEM_ID, + XML_ROLE_NOTATION_PUBLIC_ID, + XML_ROLE_ATTRIBUTE_NAME, + XML_ROLE_ATTRIBUTE_TYPE_CDATA, + XML_ROLE_ATTRIBUTE_TYPE_ID, + XML_ROLE_ATTRIBUTE_TYPE_IDREF, + XML_ROLE_ATTRIBUTE_TYPE_IDREFS, + XML_ROLE_ATTRIBUTE_TYPE_ENTITY, + XML_ROLE_ATTRIBUTE_TYPE_ENTITIES, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKEN, + XML_ROLE_ATTRIBUTE_TYPE_NMTOKENS, + XML_ROLE_ATTRIBUTE_ENUM_VALUE, + XML_ROLE_ATTRIBUTE_NOTATION_VALUE, + XML_ROLE_ATTLIST_NONE, + XML_ROLE_ATTLIST_ELEMENT_NAME, + XML_ROLE_IMPLIED_ATTRIBUTE_VALUE, + XML_ROLE_REQUIRED_ATTRIBUTE_VALUE, + XML_ROLE_DEFAULT_ATTRIBUTE_VALUE, + XML_ROLE_FIXED_ATTRIBUTE_VALUE, + XML_ROLE_ELEMENT_NONE, + XML_ROLE_ELEMENT_NAME, + XML_ROLE_CONTENT_ANY, + XML_ROLE_CONTENT_EMPTY, + XML_ROLE_CONTENT_PCDATA, + XML_ROLE_GROUP_OPEN, + XML_ROLE_GROUP_CLOSE, + XML_ROLE_GROUP_CLOSE_REP, + XML_ROLE_GROUP_CLOSE_OPT, + XML_ROLE_GROUP_CLOSE_PLUS, + XML_ROLE_GROUP_CHOICE, + XML_ROLE_GROUP_SEQUENCE, + XML_ROLE_CONTENT_ELEMENT, + XML_ROLE_CONTENT_ELEMENT_REP, + XML_ROLE_CONTENT_ELEMENT_OPT, + XML_ROLE_CONTENT_ELEMENT_PLUS, + XML_ROLE_PI, + XML_ROLE_COMMENT, +#ifdef XML_DTD + XML_ROLE_TEXT_DECL, + XML_ROLE_IGNORE_SECT, + XML_ROLE_INNER_PARAM_ENTITY_REF, +#endif /* XML_DTD */ + XML_ROLE_PARAM_ENTITY_REF +}; + +typedef struct prolog_state { + int (PTRCALL *handler) (struct prolog_state *state, + int tok, + const char *ptr, + const char *end, + const ENCODING *enc); + unsigned level; + int role_none; +#ifdef XML_DTD + unsigned includeLevel; + int documentEntity; + int inEntityValue; +#endif /* XML_DTD */ +} PROLOG_STATE; + +void XmlPrologStateInit(PROLOG_STATE *); +#ifdef XML_DTD +void XmlPrologStateInitExternalEntity(PROLOG_STATE *); +#endif /* XML_DTD */ + +#define XmlTokenRole(state, tok, ptr, end, enc) \ + (((state)->handler)(state, tok, ptr, end, enc)) + +#ifdef __cplusplus +} +#endif + +#endif /* not XmlRole_INCLUDED */ diff --git a/source/lib/expat_lib/xmltok.c b/source/lib/expat_lib/xmltok.c new file mode 100644 index 0000000..bf09dfc --- /dev/null +++ b/source/lib/expat_lib/xmltok.c @@ -0,0 +1,1651 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#include + +#ifdef COMPILED_FROM_DSP +#include "winconfig.h" +#elif defined(MACOS_CLASSIC) +#include "macconfig.h" +#elif defined(__amigaos__) +#include "amigaconfig.h" +#elif defined(__WATCOMC__) +#include "watcomconfig.h" +#else +#ifdef HAVE_EXPAT_CONFIG_H +#include +#endif +#endif /* ndef COMPILED_FROM_DSP */ + +#include "expat_external.h" +#include "internal.h" +#include "xmltok.h" +#include "nametab.h" + +#ifdef XML_DTD +#define IGNORE_SECTION_TOK_VTABLE , PREFIX(ignoreSectionTok) +#else +#define IGNORE_SECTION_TOK_VTABLE /* as nothing */ +#endif + +#define VTABLE1 \ + { PREFIX(prologTok), PREFIX(contentTok), \ + PREFIX(cdataSectionTok) IGNORE_SECTION_TOK_VTABLE }, \ + { PREFIX(attributeValueTok), PREFIX(entityValueTok) }, \ + PREFIX(sameName), \ + PREFIX(nameMatchesAscii), \ + PREFIX(nameLength), \ + PREFIX(skipS), \ + PREFIX(getAtts), \ + PREFIX(charRefNumber), \ + PREFIX(predefinedEntityName), \ + PREFIX(updatePosition), \ + PREFIX(isPublicId) + +#define VTABLE VTABLE1, PREFIX(toUtf8), PREFIX(toUtf16) + +#define UCS2_GET_NAMING(pages, hi, lo) \ + (namingBitmap[(pages[hi] << 3) + ((lo) >> 5)] & (1 << ((lo) & 0x1F))) + +/* A 2 byte UTF-8 representation splits the characters 11 bits between + the bottom 5 and 6 bits of the bytes. We need 8 bits to index into + pages, 3 bits to add to that index and 5 bits to generate the mask. +*/ +#define UTF8_GET_NAMING2(pages, byte) \ + (namingBitmap[((pages)[(((byte)[0]) >> 2) & 7] << 3) \ + + ((((byte)[0]) & 3) << 1) \ + + ((((byte)[1]) >> 5) & 1)] \ + & (1 << (((byte)[1]) & 0x1F))) + +/* A 3 byte UTF-8 representation splits the characters 16 bits between + the bottom 4, 6 and 6 bits of the bytes. We need 8 bits to index + into pages, 3 bits to add to that index and 5 bits to generate the + mask. +*/ +#define UTF8_GET_NAMING3(pages, byte) \ + (namingBitmap[((pages)[((((byte)[0]) & 0xF) << 4) \ + + ((((byte)[1]) >> 2) & 0xF)] \ + << 3) \ + + ((((byte)[1]) & 3) << 1) \ + + ((((byte)[2]) >> 5) & 1)] \ + & (1 << (((byte)[2]) & 0x1F))) + +#define UTF8_GET_NAMING(pages, p, n) \ + ((n) == 2 \ + ? UTF8_GET_NAMING2(pages, (const unsigned char *)(p)) \ + : ((n) == 3 \ + ? UTF8_GET_NAMING3(pages, (const unsigned char *)(p)) \ + : 0)) + +/* Detection of invalid UTF-8 sequences is based on Table 3.1B + of Unicode 3.2: http://www.unicode.org/unicode/reports/tr28/ + with the additional restriction of not allowing the Unicode + code points 0xFFFF and 0xFFFE (sequences EF,BF,BF and EF,BF,BE). + Implementation details: + (A & 0x80) == 0 means A < 0x80 + and + (A & 0xC0) == 0xC0 means A > 0xBF +*/ + +#define UTF8_INVALID2(p) \ + ((*p) < 0xC2 || ((p)[1] & 0x80) == 0 || ((p)[1] & 0xC0) == 0xC0) + +#define UTF8_INVALID3(p) \ + (((p)[2] & 0x80) == 0 \ + || \ + ((*p) == 0xEF && (p)[1] == 0xBF \ + ? \ + (p)[2] > 0xBD \ + : \ + ((p)[2] & 0xC0) == 0xC0) \ + || \ + ((*p) == 0xE0 \ + ? \ + (p)[1] < 0xA0 || ((p)[1] & 0xC0) == 0xC0 \ + : \ + ((p)[1] & 0x80) == 0 \ + || \ + ((*p) == 0xED ? (p)[1] > 0x9F : ((p)[1] & 0xC0) == 0xC0))) + +#define UTF8_INVALID4(p) \ + (((p)[3] & 0x80) == 0 || ((p)[3] & 0xC0) == 0xC0 \ + || \ + ((p)[2] & 0x80) == 0 || ((p)[2] & 0xC0) == 0xC0 \ + || \ + ((*p) == 0xF0 \ + ? \ + (p)[1] < 0x90 || ((p)[1] & 0xC0) == 0xC0 \ + : \ + ((p)[1] & 0x80) == 0 \ + || \ + ((*p) == 0xF4 ? (p)[1] > 0x8F : ((p)[1] & 0xC0) == 0xC0))) + +static int PTRFASTCALL +isNever(const ENCODING *enc, const char *p) +{ + return 0; +} + +static int PTRFASTCALL +utf8_isName2(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING2(namePages, (const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isName3(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING3(namePages, (const unsigned char *)p); +} + +#define utf8_isName4 isNever + +static int PTRFASTCALL +utf8_isNmstrt2(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING2(nmstrtPages, (const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isNmstrt3(const ENCODING *enc, const char *p) +{ + return UTF8_GET_NAMING3(nmstrtPages, (const unsigned char *)p); +} + +#define utf8_isNmstrt4 isNever + +static int PTRFASTCALL +utf8_isInvalid2(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID2((const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isInvalid3(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID3((const unsigned char *)p); +} + +static int PTRFASTCALL +utf8_isInvalid4(const ENCODING *enc, const char *p) +{ + return UTF8_INVALID4((const unsigned char *)p); +} + +struct normal_encoding { + ENCODING enc; + unsigned char type[256]; +#ifdef XML_MIN_SIZE + int (PTRFASTCALL *byteType)(const ENCODING *, const char *); + int (PTRFASTCALL *isNameMin)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrtMin)(const ENCODING *, const char *); + int (PTRFASTCALL *byteToAscii)(const ENCODING *, const char *); + int (PTRCALL *charMatches)(const ENCODING *, const char *, int); +#endif /* XML_MIN_SIZE */ + int (PTRFASTCALL *isName2)(const ENCODING *, const char *); + int (PTRFASTCALL *isName3)(const ENCODING *, const char *); + int (PTRFASTCALL *isName4)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt2)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt3)(const ENCODING *, const char *); + int (PTRFASTCALL *isNmstrt4)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid2)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid3)(const ENCODING *, const char *); + int (PTRFASTCALL *isInvalid4)(const ENCODING *, const char *); +}; + +#define AS_NORMAL_ENCODING(enc) ((const struct normal_encoding *) (enc)) + +#ifdef XML_MIN_SIZE + +#define STANDARD_VTABLE(E) \ + E ## byteType, \ + E ## isNameMin, \ + E ## isNmstrtMin, \ + E ## byteToAscii, \ + E ## charMatches, + +#else + +#define STANDARD_VTABLE(E) /* as nothing */ + +#endif + +#define NORMAL_VTABLE(E) \ + E ## isName2, \ + E ## isName3, \ + E ## isName4, \ + E ## isNmstrt2, \ + E ## isNmstrt3, \ + E ## isNmstrt4, \ + E ## isInvalid2, \ + E ## isInvalid3, \ + E ## isInvalid4 + +static int FASTCALL checkCharRefNumber(int); + +#include "xmltok_impl.h" +#include "ascii.h" + +#ifdef XML_MIN_SIZE +#define sb_isNameMin isNever +#define sb_isNmstrtMin isNever +#endif + +#ifdef XML_MIN_SIZE +#define MINBPC(enc) ((enc)->minBytesPerChar) +#else +/* minimum bytes per character */ +#define MINBPC(enc) 1 +#endif + +#define SB_BYTE_TYPE(enc, p) \ + (((struct normal_encoding *)(enc))->type[(unsigned char)*(p)]) + +#ifdef XML_MIN_SIZE +static int PTRFASTCALL +sb_byteType(const ENCODING *enc, const char *p) +{ + return SB_BYTE_TYPE(enc, p); +} +#define BYTE_TYPE(enc, p) \ + (AS_NORMAL_ENCODING(enc)->byteType(enc, p)) +#else +#define BYTE_TYPE(enc, p) SB_BYTE_TYPE(enc, p) +#endif + +#ifdef XML_MIN_SIZE +#define BYTE_TO_ASCII(enc, p) \ + (AS_NORMAL_ENCODING(enc)->byteToAscii(enc, p)) +static int PTRFASTCALL +sb_byteToAscii(const ENCODING *enc, const char *p) +{ + return *p; +} +#else +#define BYTE_TO_ASCII(enc, p) (*(p)) +#endif + +#define IS_NAME_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isName ## n(enc, p)) +#define IS_NMSTRT_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isNmstrt ## n(enc, p)) +#define IS_INVALID_CHAR(enc, p, n) \ + (AS_NORMAL_ENCODING(enc)->isInvalid ## n(enc, p)) + +#ifdef XML_MIN_SIZE +#define IS_NAME_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNameMin(enc, p)) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) \ + (AS_NORMAL_ENCODING(enc)->isNmstrtMin(enc, p)) +#else +#define IS_NAME_CHAR_MINBPC(enc, p) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) (0) +#endif + +#ifdef XML_MIN_SIZE +#define CHAR_MATCHES(enc, p, c) \ + (AS_NORMAL_ENCODING(enc)->charMatches(enc, p, c)) +static int PTRCALL +sb_charMatches(const ENCODING *enc, const char *p, int c) +{ + return *p == c; +} +#else +/* c is an ASCII character */ +#define CHAR_MATCHES(enc, p, c) (*(p) == c) +#endif + +#define PREFIX(ident) normal_ ## ident +#define XML_TOK_IMPL_C +#include "xmltok_impl.c" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +enum { /* UTF8_cvalN is value of masked first byte of N byte sequence */ + UTF8_cval1 = 0x00, + UTF8_cval2 = 0xc0, + UTF8_cval3 = 0xe0, + UTF8_cval4 = 0xf0 +}; + +static void PTRCALL +utf8_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + char *to; + const char *from; + if (fromLim - *fromP > toLim - *toP) { + /* Avoid copying partial characters. */ + for (fromLim = *fromP + (toLim - *toP); fromLim > *fromP; fromLim--) + if (((unsigned char)fromLim[-1] & 0xc0) != 0x80) + break; + } + for (to = *toP, from = *fromP; from != fromLim; from++, to++) + *to = *from; + *fromP = from; + *toP = to; +} + +static void PTRCALL +utf8_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + unsigned short *to = *toP; + const char *from = *fromP; + while (from != fromLim && to != toLim) { + switch (((struct normal_encoding *)enc)->type[(unsigned char)*from]) { + case BT_LEAD2: + *to++ = (unsigned short)(((from[0] & 0x1f) << 6) | (from[1] & 0x3f)); + from += 2; + break; + case BT_LEAD3: + *to++ = (unsigned short)(((from[0] & 0xf) << 12) + | ((from[1] & 0x3f) << 6) | (from[2] & 0x3f)); + from += 3; + break; + case BT_LEAD4: + { + unsigned long n; + if (to + 1 == toLim) + goto after; + n = ((from[0] & 0x7) << 18) | ((from[1] & 0x3f) << 12) + | ((from[2] & 0x3f) << 6) | (from[3] & 0x3f); + n -= 0x10000; + to[0] = (unsigned short)((n >> 10) | 0xD800); + to[1] = (unsigned short)((n & 0x3FF) | 0xDC00); + to += 2; + from += 4; + } + break; + default: + *to++ = *from++; + break; + } + } +after: + *fromP = from; + *toP = to; +} + +#ifdef XML_NS +static const struct normal_encoding utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; +#endif + +static const struct normal_encoding utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#ifdef XML_NS + +static const struct normal_encoding internal_utf8_encoding_ns = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#include "iasciitab.h" +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +#endif + +static const struct normal_encoding internal_utf8_encoding = { + { VTABLE1, utf8_toUtf8, utf8_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "utf8tab.h" + }, + STANDARD_VTABLE(sb_) NORMAL_VTABLE(utf8_) +}; + +static void PTRCALL +latin1_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + for (;;) { + unsigned char c; + if (*fromP == fromLim) + break; + c = (unsigned char)**fromP; + if (c & 0x80) { + if (toLim - *toP < 2) + break; + *(*toP)++ = (char)((c >> 6) | UTF8_cval2); + *(*toP)++ = (char)((c & 0x3f) | 0x80); + (*fromP)++; + } + else { + if (*toP == toLim) + break; + *(*toP)++ = *(*fromP)++; + } + } +} + +static void PTRCALL +latin1_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = (unsigned char)*(*fromP)++; +} + +#ifdef XML_NS + +static const struct normal_encoding latin1_encoding_ns = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) +}; + +#endif + +static const struct normal_encoding latin1_encoding = { + { VTABLE1, latin1_toUtf8, latin1_toUtf16, 1, 0, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(sb_) +}; + +static void PTRCALL +ascii_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + while (*fromP != fromLim && *toP != toLim) + *(*toP)++ = *(*fromP)++; +} + +#ifdef XML_NS + +static const struct normal_encoding ascii_encoding_ns = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#include "asciitab.h" +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) +}; + +#endif + +static const struct normal_encoding ascii_encoding = { + { VTABLE1, ascii_toUtf8, latin1_toUtf16, 1, 1, 0 }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +/* BT_NONXML == 0 */ + }, + STANDARD_VTABLE(sb_) +}; + +static int PTRFASTCALL +unicode_byte_type(char hi, char lo) +{ + switch ((unsigned char)hi) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + return BT_LEAD4; + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return BT_TRAIL; + case 0xFF: + switch ((unsigned char)lo) { + case 0xFF: + case 0xFE: + return BT_NONXML; + } + break; + } + return BT_NONASCII; +} + +#define DEFINE_UTF16_TO_UTF8(E) \ +static void PTRCALL \ +E ## toUtf8(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + char **toP, const char *toLim) \ +{ \ + const char *from; \ + for (from = *fromP; from != fromLim; from += 2) { \ + int plane; \ + unsigned char lo2; \ + unsigned char lo = GET_LO(from); \ + unsigned char hi = GET_HI(from); \ + switch (hi) { \ + case 0: \ + if (lo < 0x80) { \ + if (*toP == toLim) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = lo; \ + break; \ + } \ + /* fall through */ \ + case 0x1: case 0x2: case 0x3: \ + case 0x4: case 0x5: case 0x6: case 0x7: \ + if (toLim - *toP < 2) { \ + *fromP = from; \ + return; \ + } \ + *(*toP)++ = ((lo >> 6) | (hi << 2) | UTF8_cval2); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + default: \ + if (toLim - *toP < 3) { \ + *fromP = from; \ + return; \ + } \ + /* 16 bits divided 4, 6, 6 amongst 3 bytes */ \ + *(*toP)++ = ((hi >> 4) | UTF8_cval3); \ + *(*toP)++ = (((hi & 0xf) << 2) | (lo >> 6) | 0x80); \ + *(*toP)++ = ((lo & 0x3f) | 0x80); \ + break; \ + case 0xD8: case 0xD9: case 0xDA: case 0xDB: \ + if (toLim - *toP < 4) { \ + *fromP = from; \ + return; \ + } \ + plane = (((hi & 0x3) << 2) | ((lo >> 6) & 0x3)) + 1; \ + *(*toP)++ = ((plane >> 2) | UTF8_cval4); \ + *(*toP)++ = (((lo >> 2) & 0xF) | ((plane & 0x3) << 4) | 0x80); \ + from += 2; \ + lo2 = GET_LO(from); \ + *(*toP)++ = (((lo & 0x3) << 4) \ + | ((GET_HI(from) & 0x3) << 2) \ + | (lo2 >> 6) \ + | 0x80); \ + *(*toP)++ = ((lo2 & 0x3f) | 0x80); \ + break; \ + } \ + } \ + *fromP = from; \ +} + +#define DEFINE_UTF16_TO_UTF16(E) \ +static void PTRCALL \ +E ## toUtf16(const ENCODING *enc, \ + const char **fromP, const char *fromLim, \ + unsigned short **toP, const unsigned short *toLim) \ +{ \ + /* Avoid copying first half only of surrogate */ \ + if (fromLim - *fromP > ((toLim - *toP) << 1) \ + && (GET_HI(fromLim - 2) & 0xF8) == 0xD8) \ + fromLim -= 2; \ + for (; *fromP != fromLim && *toP != toLim; *fromP += 2) \ + *(*toP)++ = (GET_HI(*fromP) << 8) | GET_LO(*fromP); \ +} + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) & 0xff)), ((ptr)[1] = ((ch) >> 8))) +#define GET_LO(ptr) ((unsigned char)(ptr)[0]) +#define GET_HI(ptr) ((unsigned char)(ptr)[1]) + +DEFINE_UTF16_TO_UTF8(little2_) +DEFINE_UTF16_TO_UTF16(little2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define SET2(ptr, ch) \ + (((ptr)[0] = ((ch) >> 8)), ((ptr)[1] = ((ch) & 0xFF))) +#define GET_LO(ptr) ((unsigned char)(ptr)[1]) +#define GET_HI(ptr) ((unsigned char)(ptr)[0]) + +DEFINE_UTF16_TO_UTF8(big2_) +DEFINE_UTF16_TO_UTF16(big2_) + +#undef SET2 +#undef GET_LO +#undef GET_HI + +#define LITTLE2_BYTE_TYPE(enc, p) \ + ((p)[1] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)*(p)] \ + : unicode_byte_type((p)[1], (p)[0])) +#define LITTLE2_BYTE_TO_ASCII(enc, p) ((p)[1] == 0 ? (p)[0] : -1) +#define LITTLE2_CHAR_MATCHES(enc, p, c) ((p)[1] == 0 && (p)[0] == c) +#define LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[1], (unsigned char)p[0]) +#define LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[1], (unsigned char)p[0]) + +#ifdef XML_MIN_SIZE + +static int PTRFASTCALL +little2_byteType(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TYPE(enc, p); +} + +static int PTRFASTCALL +little2_byteToAscii(const ENCODING *enc, const char *p) +{ + return LITTLE2_BYTE_TO_ASCII(enc, p); +} + +static int PTRCALL +little2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return LITTLE2_CHAR_MATCHES(enc, p, c); +} + +static int PTRFASTCALL +little2_isNameMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static int PTRFASTCALL +little2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, little2_toUtf8, little2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) little2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) LITTLE2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) LITTLE2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) LITTLE2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) LITTLE2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) LITTLE2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#define XML_TOK_IMPL_C +#include "xmltok_impl.c" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding little2_encoding_ns = { + { VTABLE, 2, 0, +#if BYTEORDER == 1234 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + +static const struct normal_encoding little2_encoding = { + { VTABLE, 2, 0, +#if BYTEORDER == 1234 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#if BYTEORDER != 4321 + +#ifdef XML_NS + +static const struct normal_encoding internal_little2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + +static const struct normal_encoding internal_little2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(little2_) +}; + +#endif + + +#define BIG2_BYTE_TYPE(enc, p) \ + ((p)[0] == 0 \ + ? ((struct normal_encoding *)(enc))->type[(unsigned char)(p)[1]] \ + : unicode_byte_type((p)[0], (p)[1])) +#define BIG2_BYTE_TO_ASCII(enc, p) ((p)[0] == 0 ? (p)[1] : -1) +#define BIG2_CHAR_MATCHES(enc, p, c) ((p)[0] == 0 && (p)[1] == c) +#define BIG2_IS_NAME_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(namePages, (unsigned char)p[0], (unsigned char)p[1]) +#define BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) \ + UCS2_GET_NAMING(nmstrtPages, (unsigned char)p[0], (unsigned char)p[1]) + +#ifdef XML_MIN_SIZE + +static int PTRFASTCALL +big2_byteType(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TYPE(enc, p); +} + +static int PTRFASTCALL +big2_byteToAscii(const ENCODING *enc, const char *p) +{ + return BIG2_BYTE_TO_ASCII(enc, p); +} + +static int PTRCALL +big2_charMatches(const ENCODING *enc, const char *p, int c) +{ + return BIG2_CHAR_MATCHES(enc, p, c); +} + +static int PTRFASTCALL +big2_isNameMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NAME_CHAR_MINBPC(enc, p); +} + +static int PTRFASTCALL +big2_isNmstrtMin(const ENCODING *enc, const char *p) +{ + return BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p); +} + +#undef VTABLE +#define VTABLE VTABLE1, big2_toUtf8, big2_toUtf16 + +#else /* not XML_MIN_SIZE */ + +#undef PREFIX +#define PREFIX(ident) big2_ ## ident +#define MINBPC(enc) 2 +/* CHAR_MATCHES is guaranteed to have MINBPC bytes available. */ +#define BYTE_TYPE(enc, p) BIG2_BYTE_TYPE(enc, p) +#define BYTE_TO_ASCII(enc, p) BIG2_BYTE_TO_ASCII(enc, p) +#define CHAR_MATCHES(enc, p, c) BIG2_CHAR_MATCHES(enc, p, c) +#define IS_NAME_CHAR(enc, p, n) 0 +#define IS_NAME_CHAR_MINBPC(enc, p) BIG2_IS_NAME_CHAR_MINBPC(enc, p) +#define IS_NMSTRT_CHAR(enc, p, n) (0) +#define IS_NMSTRT_CHAR_MINBPC(enc, p) BIG2_IS_NMSTRT_CHAR_MINBPC(enc, p) + +#define XML_TOK_IMPL_C +#include "xmltok_impl.c" +#undef XML_TOK_IMPL_C + +#undef MINBPC +#undef BYTE_TYPE +#undef BYTE_TO_ASCII +#undef CHAR_MATCHES +#undef IS_NAME_CHAR +#undef IS_NAME_CHAR_MINBPC +#undef IS_NMSTRT_CHAR +#undef IS_NMSTRT_CHAR_MINBPC +#undef IS_INVALID_CHAR + +#endif /* not XML_MIN_SIZE */ + +#ifdef XML_NS + +static const struct normal_encoding big2_encoding_ns = { + { VTABLE, 2, 0, +#if BYTEORDER == 4321 + 1 +#else + 0 +#endif + }, + { +#include "asciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +static const struct normal_encoding big2_encoding = { + { VTABLE, 2, 0, +#if BYTEORDER == 4321 + 1 +#else + 0 +#endif + }, + { +#define BT_COLON BT_NMSTRT +#include "asciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#if BYTEORDER != 1234 + +#ifdef XML_NS + +static const struct normal_encoding internal_big2_encoding_ns = { + { VTABLE, 2, 0, 1 }, + { +#include "iasciitab.h" +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +static const struct normal_encoding internal_big2_encoding = { + { VTABLE, 2, 0, 1 }, + { +#define BT_COLON BT_NMSTRT +#include "iasciitab.h" +#undef BT_COLON +#include "latin1tab.h" + }, + STANDARD_VTABLE(big2_) +}; + +#endif + +#undef PREFIX + +static int FASTCALL +streqci(const char *s1, const char *s2) +{ + for (;;) { + char c1 = *s1++; + char c2 = *s2++; + if (ASCII_a <= c1 && c1 <= ASCII_z) + c1 += ASCII_A - ASCII_a; + if (ASCII_a <= c2 && c2 <= ASCII_z) + c2 += ASCII_A - ASCII_a; + if (c1 != c2) + return 0; + if (!c1) + break; + } + return 1; +} + +static void PTRCALL +initUpdatePosition(const ENCODING *enc, const char *ptr, + const char *end, POSITION *pos) +{ + normal_updatePosition(&utf8_encoding.enc, ptr, end, pos); +} + +static int +toAscii(const ENCODING *enc, const char *ptr, const char *end) +{ + char buf[1]; + char *p = buf; + XmlUtf8Convert(enc, &ptr, end, &p, p + 1); + if (p == buf) + return -1; + else + return buf[0]; +} + +static int FASTCALL +isSpace(int c) +{ + switch (c) { + case 0x20: + case 0xD: + case 0xA: + case 0x9: + return 1; + } + return 0; +} + +/* Return 1 if there's just optional white space or there's an S + followed by name=val. +*/ +static int +parsePseudoAttribute(const ENCODING *enc, + const char *ptr, + const char *end, + const char **namePtr, + const char **nameEndPtr, + const char **valPtr, + const char **nextTokPtr) +{ + int c; + char open; + if (ptr == end) { + *namePtr = NULL; + return 1; + } + if (!isSpace(toAscii(enc, ptr, end))) { + *nextTokPtr = ptr; + return 0; + } + do { + ptr += enc->minBytesPerChar; + } while (isSpace(toAscii(enc, ptr, end))); + if (ptr == end) { + *namePtr = NULL; + return 1; + } + *namePtr = ptr; + for (;;) { + c = toAscii(enc, ptr, end); + if (c == -1) { + *nextTokPtr = ptr; + return 0; + } + if (c == ASCII_EQUALS) { + *nameEndPtr = ptr; + break; + } + if (isSpace(c)) { + *nameEndPtr = ptr; + do { + ptr += enc->minBytesPerChar; + } while (isSpace(c = toAscii(enc, ptr, end))); + if (c != ASCII_EQUALS) { + *nextTokPtr = ptr; + return 0; + } + break; + } + ptr += enc->minBytesPerChar; + } + if (ptr == *namePtr) { + *nextTokPtr = ptr; + return 0; + } + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + while (isSpace(c)) { + ptr += enc->minBytesPerChar; + c = toAscii(enc, ptr, end); + } + if (c != ASCII_QUOT && c != ASCII_APOS) { + *nextTokPtr = ptr; + return 0; + } + open = (char)c; + ptr += enc->minBytesPerChar; + *valPtr = ptr; + for (;; ptr += enc->minBytesPerChar) { + c = toAscii(enc, ptr, end); + if (c == open) + break; + if (!(ASCII_a <= c && c <= ASCII_z) + && !(ASCII_A <= c && c <= ASCII_Z) + && !(ASCII_0 <= c && c <= ASCII_9) + && c != ASCII_PERIOD + && c != ASCII_MINUS + && c != ASCII_UNDERSCORE) { + *nextTokPtr = ptr; + return 0; + } + } + *nextTokPtr = ptr + enc->minBytesPerChar; + return 1; +} + +static const char KW_version[] = { + ASCII_v, ASCII_e, ASCII_r, ASCII_s, ASCII_i, ASCII_o, ASCII_n, '\0' +}; + +static const char KW_encoding[] = { + ASCII_e, ASCII_n, ASCII_c, ASCII_o, ASCII_d, ASCII_i, ASCII_n, ASCII_g, '\0' +}; + +static const char KW_standalone[] = { + ASCII_s, ASCII_t, ASCII_a, ASCII_n, ASCII_d, ASCII_a, ASCII_l, ASCII_o, + ASCII_n, ASCII_e, '\0' +}; + +static const char KW_yes[] = { + ASCII_y, ASCII_e, ASCII_s, '\0' +}; + +static const char KW_no[] = { + ASCII_n, ASCII_o, '\0' +}; + +static int +doParseXmlDecl(const ENCODING *(*encodingFinder)(const ENCODING *, + const char *, + const char *), + int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + const char *val = NULL; + const char *name = NULL; + const char *nameEnd = NULL; + ptr += 5 * enc->minBytesPerChar; + end -= 2 * enc->minBytesPerChar; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr) + || !name) { + *badPtr = ptr; + return 0; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_version)) { + if (!isGeneralTextEntity) { + *badPtr = name; + return 0; + } + } + else { + if (versionPtr) + *versionPtr = val; + if (versionEndPtr) + *versionEndPtr = ptr; + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) { + if (isGeneralTextEntity) { + /* a TextDecl must have an EncodingDecl */ + *badPtr = ptr; + return 0; + } + return 1; + } + } + if (XmlNameMatchesAscii(enc, name, nameEnd, KW_encoding)) { + int c = toAscii(enc, val, end); + if (!(ASCII_a <= c && c <= ASCII_z) && !(ASCII_A <= c && c <= ASCII_Z)) { + *badPtr = val; + return 0; + } + if (encodingName) + *encodingName = val; + if (encoding) + *encoding = encodingFinder(enc, val, ptr - enc->minBytesPerChar); + if (!parsePseudoAttribute(enc, ptr, end, &name, &nameEnd, &val, &ptr)) { + *badPtr = ptr; + return 0; + } + if (!name) + return 1; + } + if (!XmlNameMatchesAscii(enc, name, nameEnd, KW_standalone) + || isGeneralTextEntity) { + *badPtr = name; + return 0; + } + if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_yes)) { + if (standalone) + *standalone = 1; + } + else if (XmlNameMatchesAscii(enc, val, ptr - enc->minBytesPerChar, KW_no)) { + if (standalone) + *standalone = 0; + } + else { + *badPtr = val; + return 0; + } + while (isSpace(toAscii(enc, ptr, end))) + ptr += enc->minBytesPerChar; + if (ptr != end) { + *badPtr = ptr; + return 0; + } + return 1; +} + +static int FASTCALL +checkCharRefNumber(int result) +{ + switch (result >> 8) { + case 0xD8: case 0xD9: case 0xDA: case 0xDB: + case 0xDC: case 0xDD: case 0xDE: case 0xDF: + return -1; + case 0: + if (latin1_encoding.type[result] == BT_NONXML) + return -1; + break; + case 0xFF: + if (result == 0xFFFE || result == 0xFFFF) + return -1; + break; + } + return result; +} + +int FASTCALL +XmlUtf8Encode(int c, char *buf) +{ + enum { + /* minN is minimum legal resulting value for N byte sequence */ + min2 = 0x80, + min3 = 0x800, + min4 = 0x10000 + }; + + if (c < 0) + return 0; + if (c < min2) { + buf[0] = (char)(c | UTF8_cval1); + return 1; + } + if (c < min3) { + buf[0] = (char)((c >> 6) | UTF8_cval2); + buf[1] = (char)((c & 0x3f) | 0x80); + return 2; + } + if (c < min4) { + buf[0] = (char)((c >> 12) | UTF8_cval3); + buf[1] = (char)(((c >> 6) & 0x3f) | 0x80); + buf[2] = (char)((c & 0x3f) | 0x80); + return 3; + } + if (c < 0x110000) { + buf[0] = (char)((c >> 18) | UTF8_cval4); + buf[1] = (char)(((c >> 12) & 0x3f) | 0x80); + buf[2] = (char)(((c >> 6) & 0x3f) | 0x80); + buf[3] = (char)((c & 0x3f) | 0x80); + return 4; + } + return 0; +} + +int FASTCALL +XmlUtf16Encode(int charNum, unsigned short *buf) +{ + if (charNum < 0) + return 0; + if (charNum < 0x10000) { + buf[0] = (unsigned short)charNum; + return 1; + } + if (charNum < 0x110000) { + charNum -= 0x10000; + buf[0] = (unsigned short)((charNum >> 10) + 0xD800); + buf[1] = (unsigned short)((charNum & 0x3FF) + 0xDC00); + return 2; + } + return 0; +} + +struct unknown_encoding { + struct normal_encoding normal; + CONVERTER convert; + void *userData; + unsigned short utf16[256]; + char utf8[256][4]; +}; + +#define AS_UNKNOWN_ENCODING(enc) ((const struct unknown_encoding *) (enc)) + +int +XmlSizeOfUnknownEncoding(void) +{ + return sizeof(struct unknown_encoding); +} + +static int PTRFASTCALL +unknown_isName(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(namePages, c >> 8, c & 0xFF); +} + +static int PTRFASTCALL +unknown_isNmstrt(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + if (c & ~0xFFFF) + return 0; + return UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xFF); +} + +static int PTRFASTCALL +unknown_isInvalid(const ENCODING *enc, const char *p) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + int c = uenc->convert(uenc->userData, p); + return (c & ~0xFFFF) || checkCharRefNumber(c) < 0; +} + +static void PTRCALL +unknown_toUtf8(const ENCODING *enc, + const char **fromP, const char *fromLim, + char **toP, const char *toLim) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + char buf[XML_UTF8_ENCODE_MAX]; + for (;;) { + const char *utf8; + int n; + if (*fromP == fromLim) + break; + utf8 = uenc->utf8[(unsigned char)**fromP]; + n = *utf8++; + if (n == 0) { + int c = uenc->convert(uenc->userData, *fromP); + n = XmlUtf8Encode(c, buf); + if (n > toLim - *toP) + break; + utf8 = buf; + *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2)); + } + else { + if (n > toLim - *toP) + break; + (*fromP)++; + } + do { + *(*toP)++ = *utf8++; + } while (--n != 0); + } +} + +static void PTRCALL +unknown_toUtf16(const ENCODING *enc, + const char **fromP, const char *fromLim, + unsigned short **toP, const unsigned short *toLim) +{ + const struct unknown_encoding *uenc = AS_UNKNOWN_ENCODING(enc); + while (*fromP != fromLim && *toP != toLim) { + unsigned short c = uenc->utf16[(unsigned char)**fromP]; + if (c == 0) { + c = (unsigned short) + uenc->convert(uenc->userData, *fromP); + *fromP += (AS_NORMAL_ENCODING(enc)->type[(unsigned char)**fromP] + - (BT_LEAD2 - 2)); + } + else + (*fromP)++; + *(*toP)++ = c; + } +} + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + CONVERTER convert, + void *userData) +{ + int i; + struct unknown_encoding *e = (struct unknown_encoding *)mem; + for (i = 0; i < (int)sizeof(struct normal_encoding); i++) + ((char *)mem)[i] = ((char *)&latin1_encoding)[i]; + for (i = 0; i < 128; i++) + if (latin1_encoding.type[i] != BT_OTHER + && latin1_encoding.type[i] != BT_NONXML + && table[i] != i) + return 0; + for (i = 0; i < 256; i++) { + int c = table[i]; + if (c == -1) { + e->normal.type[i] = BT_MALFORM; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else if (c < 0) { + if (c < -4) + return 0; + e->normal.type[i] = (unsigned char)(BT_LEAD2 - (c + 2)); + e->utf8[i][0] = 0; + e->utf16[i] = 0; + } + else if (c < 0x80) { + if (latin1_encoding.type[c] != BT_OTHER + && latin1_encoding.type[c] != BT_NONXML + && c != i) + return 0; + e->normal.type[i] = latin1_encoding.type[c]; + e->utf8[i][0] = 1; + e->utf8[i][1] = (char)c; + e->utf16[i] = (unsigned short)(c == 0 ? 0xFFFF : c); + } + else if (checkCharRefNumber(c) < 0) { + e->normal.type[i] = BT_NONXML; + /* This shouldn't really get used. */ + e->utf16[i] = 0xFFFF; + e->utf8[i][0] = 1; + e->utf8[i][1] = 0; + } + else { + if (c > 0xFFFF) + return 0; + if (UCS2_GET_NAMING(nmstrtPages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NMSTRT; + else if (UCS2_GET_NAMING(namePages, c >> 8, c & 0xff)) + e->normal.type[i] = BT_NAME; + else + e->normal.type[i] = BT_OTHER; + e->utf8[i][0] = (char)XmlUtf8Encode(c, e->utf8[i] + 1); + e->utf16[i] = (unsigned short)c; + } + } + e->userData = userData; + e->convert = convert; + if (convert) { + e->normal.isName2 = unknown_isName; + e->normal.isName3 = unknown_isName; + e->normal.isName4 = unknown_isName; + e->normal.isNmstrt2 = unknown_isNmstrt; + e->normal.isNmstrt3 = unknown_isNmstrt; + e->normal.isNmstrt4 = unknown_isNmstrt; + e->normal.isInvalid2 = unknown_isInvalid; + e->normal.isInvalid3 = unknown_isInvalid; + e->normal.isInvalid4 = unknown_isInvalid; + } + e->normal.enc.utf8Convert = unknown_toUtf8; + e->normal.enc.utf16Convert = unknown_toUtf16; + return &(e->normal.enc); +} + +/* If this enumeration is changed, getEncodingIndex and encodings +must also be changed. */ +enum { + UNKNOWN_ENC = -1, + ISO_8859_1_ENC = 0, + US_ASCII_ENC, + UTF_8_ENC, + UTF_16_ENC, + UTF_16BE_ENC, + UTF_16LE_ENC, + /* must match encodingNames up to here */ + NO_ENC +}; + +static const char KW_ISO_8859_1[] = { + ASCII_I, ASCII_S, ASCII_O, ASCII_MINUS, ASCII_8, ASCII_8, ASCII_5, ASCII_9, + ASCII_MINUS, ASCII_1, '\0' +}; +static const char KW_US_ASCII[] = { + ASCII_U, ASCII_S, ASCII_MINUS, ASCII_A, ASCII_S, ASCII_C, ASCII_I, ASCII_I, + '\0' +}; +static const char KW_UTF_8[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_8, '\0' +}; +static const char KW_UTF_16[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, '\0' +}; +static const char KW_UTF_16BE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_B, ASCII_E, + '\0' +}; +static const char KW_UTF_16LE[] = { + ASCII_U, ASCII_T, ASCII_F, ASCII_MINUS, ASCII_1, ASCII_6, ASCII_L, ASCII_E, + '\0' +}; + +static int FASTCALL +getEncodingIndex(const char *name) +{ + static const char * const encodingNames[] = { + KW_ISO_8859_1, + KW_US_ASCII, + KW_UTF_8, + KW_UTF_16, + KW_UTF_16BE, + KW_UTF_16LE, + }; + int i; + if (name == NULL) + return NO_ENC; + for (i = 0; i < (int)(sizeof(encodingNames)/sizeof(encodingNames[0])); i++) + if (streqci(name, encodingNames[i])) + return i; + return UNKNOWN_ENC; +} + +/* For binary compatibility, we store the index of the encoding + specified at initialization in the isUtf16 member. +*/ + +#define INIT_ENC_INDEX(enc) ((int)(enc)->initEnc.isUtf16) +#define SET_INIT_ENC_INDEX(enc, i) ((enc)->initEnc.isUtf16 = (char)i) + +/* This is what detects the encoding. encodingTable maps from + encoding indices to encodings; INIT_ENC_INDEX(enc) is the index of + the external (protocol) specified encoding; state is + XML_CONTENT_STATE if we're parsing an external text entity, and + XML_PROLOG_STATE otherwise. +*/ + + +static int +initScan(const ENCODING * const *encodingTable, + const INIT_ENCODING *enc, + int state, + const char *ptr, + const char *end, + const char **nextTokPtr) +{ + const ENCODING **encPtr; + + if (ptr == end) + return XML_TOK_NONE; + encPtr = enc->encPtr; + if (ptr + 1 == end) { + /* only a single byte available for auto-detection */ +#ifndef XML_DTD /* FIXME */ + /* a well-formed document entity must have more than one byte */ + if (state != XML_CONTENT_STATE) + return XML_TOK_PARTIAL; +#endif + /* so we're parsing an external text entity... */ + /* if UTF-16 was externally specified, then we need at least 2 bytes */ + switch (INIT_ENC_INDEX(enc)) { + case UTF_16_ENC: + case UTF_16LE_ENC: + case UTF_16BE_ENC: + return XML_TOK_PARTIAL; + } + switch ((unsigned char)*ptr) { + case 0xFE: + case 0xFF: + case 0xEF: /* possibly first byte of UTF-8 BOM */ + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + /* fall through */ + case 0x00: + case 0x3C: + return XML_TOK_PARTIAL; + } + } + else { + switch (((unsigned char)ptr[0] << 8) | (unsigned char)ptr[1]) { + case 0xFEFF: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XML_TOK_BOM; + /* 00 3C is handled in the default case */ + case 0x3C00: + if ((INIT_ENC_INDEX(enc) == UTF_16BE_ENC + || INIT_ENC_INDEX(enc) == UTF_16_ENC) + && state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + case 0xFFFE: + if (INIT_ENC_INDEX(enc) == ISO_8859_1_ENC + && state == XML_CONTENT_STATE) + break; + *nextTokPtr = ptr + 2; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XML_TOK_BOM; + case 0xEFBB: + /* Maybe a UTF-8 BOM (EF BB BF) */ + /* If there's an explicitly specified (external) encoding + of ISO-8859-1 or some flavour of UTF-16 + and this is an external text entity, + don't look for the BOM, + because it might be a legal data. + */ + if (state == XML_CONTENT_STATE) { + int e = INIT_ENC_INDEX(enc); + if (e == ISO_8859_1_ENC || e == UTF_16BE_ENC + || e == UTF_16LE_ENC || e == UTF_16_ENC) + break; + } + if (ptr + 2 == end) + return XML_TOK_PARTIAL; + if ((unsigned char)ptr[2] == 0xBF) { + *nextTokPtr = ptr + 3; + *encPtr = encodingTable[UTF_8_ENC]; + return XML_TOK_BOM; + } + break; + default: + if (ptr[0] == '\0') { + /* 0 isn't a legal data character. Furthermore a document + entity can only start with ASCII characters. So the only + way this can fail to be big-endian UTF-16 if it it's an + external parsed general entity that's labelled as + UTF-16LE. + */ + if (state == XML_CONTENT_STATE && INIT_ENC_INDEX(enc) == UTF_16LE_ENC) + break; + *encPtr = encodingTable[UTF_16BE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + else if (ptr[1] == '\0') { + /* We could recover here in the case: + - parsing an external entity + - second byte is 0 + - no externally specified encoding + - no encoding declaration + by assuming UTF-16LE. But we don't, because this would mean when + presented just with a single byte, we couldn't reliably determine + whether we needed further bytes. + */ + if (state == XML_CONTENT_STATE) + break; + *encPtr = encodingTable[UTF_16LE_ENC]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); + } + break; + } + } + *encPtr = encodingTable[INIT_ENC_INDEX(enc)]; + return XmlTok(*encPtr, state, ptr, end, nextTokPtr); +} + + +#define NS(x) x +#define ns(x) x +#define XML_TOK_NS_C +#include "xmltok_ns.c" +#undef XML_TOK_NS_C +#undef NS +#undef ns + +#ifdef XML_NS + +#define NS(x) x ## NS +#define ns(x) x ## _ns + +#define XML_TOK_NS_C +#include "xmltok_ns.c" +#undef XML_TOK_NS_C + +#undef NS +#undef ns + +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + CONVERTER convert, + void *userData) +{ + ENCODING *enc = XmlInitUnknownEncoding(mem, table, convert, userData); + if (enc) + ((struct normal_encoding *)enc)->type[ASCII_COLON] = BT_COLON; + return enc; +} + +#endif /* XML_NS */ diff --git a/source/lib/expat_lib/xmltok.h b/source/lib/expat_lib/xmltok.h new file mode 100644 index 0000000..ca867aa --- /dev/null +++ b/source/lib/expat_lib/xmltok.h @@ -0,0 +1,316 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +#ifndef XmlTok_INCLUDED +#define XmlTok_INCLUDED 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/* The following token may be returned by XmlContentTok */ +#define XML_TOK_TRAILING_RSQB -5 /* ] or ]] at the end of the scan; might be + start of illegal ]]> sequence */ +/* The following tokens may be returned by both XmlPrologTok and + XmlContentTok. +*/ +#define XML_TOK_NONE -4 /* The string to be scanned is empty */ +#define XML_TOK_TRAILING_CR -3 /* A CR at the end of the scan; + might be part of CRLF sequence */ +#define XML_TOK_PARTIAL_CHAR -2 /* only part of a multibyte sequence */ +#define XML_TOK_PARTIAL -1 /* only part of a token */ +#define XML_TOK_INVALID 0 + +/* The following tokens are returned by XmlContentTok; some are also + returned by XmlAttributeValueTok, XmlEntityTok, XmlCdataSectionTok. +*/ +#define XML_TOK_START_TAG_WITH_ATTS 1 +#define XML_TOK_START_TAG_NO_ATTS 2 +#define XML_TOK_EMPTY_ELEMENT_WITH_ATTS 3 /* empty element tag */ +#define XML_TOK_EMPTY_ELEMENT_NO_ATTS 4 +#define XML_TOK_END_TAG 5 +#define XML_TOK_DATA_CHARS 6 +#define XML_TOK_DATA_NEWLINE 7 +#define XML_TOK_CDATA_SECT_OPEN 8 +#define XML_TOK_ENTITY_REF 9 +#define XML_TOK_CHAR_REF 10 /* numeric character reference */ + +/* The following tokens may be returned by both XmlPrologTok and + XmlContentTok. +*/ +#define XML_TOK_PI 11 /* processing instruction */ +#define XML_TOK_XML_DECL 12 /* XML decl or text decl */ +#define XML_TOK_COMMENT 13 +#define XML_TOK_BOM 14 /* Byte order mark */ + +/* The following tokens are returned only by XmlPrologTok */ +#define XML_TOK_PROLOG_S 15 +#define XML_TOK_DECL_OPEN 16 /* */ +#define XML_TOK_NAME 18 +#define XML_TOK_NMTOKEN 19 +#define XML_TOK_POUND_NAME 20 /* #name */ +#define XML_TOK_OR 21 /* | */ +#define XML_TOK_PERCENT 22 +#define XML_TOK_OPEN_PAREN 23 +#define XML_TOK_CLOSE_PAREN 24 +#define XML_TOK_OPEN_BRACKET 25 +#define XML_TOK_CLOSE_BRACKET 26 +#define XML_TOK_LITERAL 27 +#define XML_TOK_PARAM_ENTITY_REF 28 +#define XML_TOK_INSTANCE_START 29 + +/* The following occur only in element type declarations */ +#define XML_TOK_NAME_QUESTION 30 /* name? */ +#define XML_TOK_NAME_ASTERISK 31 /* name* */ +#define XML_TOK_NAME_PLUS 32 /* name+ */ +#define XML_TOK_COND_SECT_OPEN 33 /* */ +#define XML_TOK_CLOSE_PAREN_QUESTION 35 /* )? */ +#define XML_TOK_CLOSE_PAREN_ASTERISK 36 /* )* */ +#define XML_TOK_CLOSE_PAREN_PLUS 37 /* )+ */ +#define XML_TOK_COMMA 38 + +/* The following token is returned only by XmlAttributeValueTok */ +#define XML_TOK_ATTRIBUTE_VALUE_S 39 + +/* The following token is returned only by XmlCdataSectionTok */ +#define XML_TOK_CDATA_SECT_CLOSE 40 + +/* With namespace processing this is returned by XmlPrologTok for a + name with a colon. +*/ +#define XML_TOK_PREFIXED_NAME 41 + +#ifdef XML_DTD +#define XML_TOK_IGNORE_SECT 42 +#endif /* XML_DTD */ + +#ifdef XML_DTD +#define XML_N_STATES 4 +#else /* not XML_DTD */ +#define XML_N_STATES 3 +#endif /* not XML_DTD */ + +#define XML_PROLOG_STATE 0 +#define XML_CONTENT_STATE 1 +#define XML_CDATA_SECTION_STATE 2 +#ifdef XML_DTD +#define XML_IGNORE_SECTION_STATE 3 +#endif /* XML_DTD */ + +#define XML_N_LITERAL_TYPES 2 +#define XML_ATTRIBUTE_VALUE_LITERAL 0 +#define XML_ENTITY_VALUE_LITERAL 1 + +/* The size of the buffer passed to XmlUtf8Encode must be at least this. */ +#define XML_UTF8_ENCODE_MAX 4 +/* The size of the buffer passed to XmlUtf16Encode must be at least this. */ +#define XML_UTF16_ENCODE_MAX 2 + +typedef struct position { + /* first line and first column are 0 not 1 */ + XML_Size lineNumber; + XML_Size columnNumber; +} POSITION; + +typedef struct { + const char *name; + const char *valuePtr; + const char *valueEnd; + char normalized; +} ATTRIBUTE; + +struct encoding; +typedef struct encoding ENCODING; + +typedef int (PTRCALL *SCANNER)(const ENCODING *, + const char *, + const char *, + const char **); + +struct encoding { + SCANNER scanners[XML_N_STATES]; + SCANNER literalScanners[XML_N_LITERAL_TYPES]; + int (PTRCALL *sameName)(const ENCODING *, + const char *, + const char *); + int (PTRCALL *nameMatchesAscii)(const ENCODING *, + const char *, + const char *, + const char *); + int (PTRFASTCALL *nameLength)(const ENCODING *, const char *); + const char *(PTRFASTCALL *skipS)(const ENCODING *, const char *); + int (PTRCALL *getAtts)(const ENCODING *enc, + const char *ptr, + int attsMax, + ATTRIBUTE *atts); + int (PTRFASTCALL *charRefNumber)(const ENCODING *enc, const char *ptr); + int (PTRCALL *predefinedEntityName)(const ENCODING *, + const char *, + const char *); + void (PTRCALL *updatePosition)(const ENCODING *, + const char *ptr, + const char *end, + POSITION *); + int (PTRCALL *isPublicId)(const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr); + void (PTRCALL *utf8Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + char **toP, + const char *toLim); + void (PTRCALL *utf16Convert)(const ENCODING *enc, + const char **fromP, + const char *fromLim, + unsigned short **toP, + const unsigned short *toLim); + int minBytesPerChar; + char isUtf8; + char isUtf16; +}; + +/* Scan the string starting at ptr until the end of the next complete + token, but do not scan past eptr. Return an integer giving the + type of token. + + Return XML_TOK_NONE when ptr == eptr; nextTokPtr will not be set. + + Return XML_TOK_PARTIAL when the string does not contain a complete + token; nextTokPtr will not be set. + + Return XML_TOK_INVALID when the string does not start a valid + token; nextTokPtr will be set to point to the character which made + the token invalid. + + Otherwise the string starts with a valid token; nextTokPtr will be + set to point to the character following the end of that token. + + Each data character counts as a single token, but adjacent data + characters may be returned together. Similarly for characters in + the prolog outside literals, comments and processing instructions. +*/ + + +#define XmlTok(enc, state, ptr, end, nextTokPtr) \ + (((enc)->scanners[state])(enc, ptr, end, nextTokPtr)) + +#define XmlPrologTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_PROLOG_STATE, ptr, end, nextTokPtr) + +#define XmlContentTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CONTENT_STATE, ptr, end, nextTokPtr) + +#define XmlCdataSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_CDATA_SECTION_STATE, ptr, end, nextTokPtr) + +#ifdef XML_DTD + +#define XmlIgnoreSectionTok(enc, ptr, end, nextTokPtr) \ + XmlTok(enc, XML_IGNORE_SECTION_STATE, ptr, end, nextTokPtr) + +#endif /* XML_DTD */ + +/* This is used for performing a 2nd-level tokenization on the content + of a literal that has already been returned by XmlTok. +*/ +#define XmlLiteralTok(enc, literalType, ptr, end, nextTokPtr) \ + (((enc)->literalScanners[literalType])(enc, ptr, end, nextTokPtr)) + +#define XmlAttributeValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ATTRIBUTE_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlEntityValueTok(enc, ptr, end, nextTokPtr) \ + XmlLiteralTok(enc, XML_ENTITY_VALUE_LITERAL, ptr, end, nextTokPtr) + +#define XmlSameName(enc, ptr1, ptr2) (((enc)->sameName)(enc, ptr1, ptr2)) + +#define XmlNameMatchesAscii(enc, ptr1, end1, ptr2) \ + (((enc)->nameMatchesAscii)(enc, ptr1, end1, ptr2)) + +#define XmlNameLength(enc, ptr) \ + (((enc)->nameLength)(enc, ptr)) + +#define XmlSkipS(enc, ptr) \ + (((enc)->skipS)(enc, ptr)) + +#define XmlGetAttributes(enc, ptr, attsMax, atts) \ + (((enc)->getAtts)(enc, ptr, attsMax, atts)) + +#define XmlCharRefNumber(enc, ptr) \ + (((enc)->charRefNumber)(enc, ptr)) + +#define XmlPredefinedEntityName(enc, ptr, end) \ + (((enc)->predefinedEntityName)(enc, ptr, end)) + +#define XmlUpdatePosition(enc, ptr, end, pos) \ + (((enc)->updatePosition)(enc, ptr, end, pos)) + +#define XmlIsPublicId(enc, ptr, end, badPtr) \ + (((enc)->isPublicId)(enc, ptr, end, badPtr)) + +#define XmlUtf8Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf8Convert)(enc, fromP, fromLim, toP, toLim)) + +#define XmlUtf16Convert(enc, fromP, fromLim, toP, toLim) \ + (((enc)->utf16Convert)(enc, fromP, fromLim, toP, toLim)) + +typedef struct { + ENCODING initEnc; + const ENCODING **encPtr; +} INIT_ENCODING; + +int XmlParseXmlDecl(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XmlInitEncoding(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING *XmlGetUtf8InternalEncoding(void); +const ENCODING *XmlGetUtf16InternalEncoding(void); +int FASTCALL XmlUtf8Encode(int charNumber, char *buf); +int FASTCALL XmlUtf16Encode(int charNumber, unsigned short *buf); +int XmlSizeOfUnknownEncoding(void); + + +typedef int (XMLCALL *CONVERTER) (void *userData, const char *p); + +ENCODING * +XmlInitUnknownEncoding(void *mem, + int *table, + CONVERTER convert, + void *userData); + +int XmlParseXmlDeclNS(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingNamePtr, + const ENCODING **namedEncodingPtr, + int *standalonePtr); + +int XmlInitEncodingNS(INIT_ENCODING *, const ENCODING **, const char *name); +const ENCODING *XmlGetUtf8InternalEncodingNS(void); +const ENCODING *XmlGetUtf16InternalEncodingNS(void); +ENCODING * +XmlInitUnknownEncodingNS(void *mem, + int *table, + CONVERTER convert, + void *userData); +#ifdef __cplusplus +} +#endif + +#endif /* not XmlTok_INCLUDED */ diff --git a/source/lib/expat_lib/xmltok_impl.c b/source/lib/expat_lib/xmltok_impl.c new file mode 100644 index 0000000..9c2895b --- /dev/null +++ b/source/lib/expat_lib/xmltok_impl.c @@ -0,0 +1,1783 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* This file is included! */ +#ifdef XML_TOK_IMPL_C + +#ifndef IS_INVALID_CHAR +#define IS_INVALID_CHAR(enc, ptr, n) (0) +#endif + +#define INVALID_LEAD_CASE(n, ptr, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_INVALID_CHAR(enc, ptr, n)) { \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define INVALID_CASES(ptr, nextTokPtr) \ + INVALID_LEAD_CASE(2, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(3, ptr, nextTokPtr) \ + INVALID_LEAD_CASE(4, ptr, nextTokPtr) \ + case BT_NONXML: \ + case BT_MALFORM: \ + case BT_TRAIL: \ + *(nextTokPtr) = (ptr); \ + return XML_TOK_INVALID; + +#define CHECK_NAME_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NAME_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NAME_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + case BT_DIGIT: \ + case BT_NAME: \ + case BT_MINUS: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NAME_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NAME_CASE(4, enc, ptr, end, nextTokPtr) + +#define CHECK_NMSTRT_CASE(n, enc, ptr, end, nextTokPtr) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (!IS_NMSTRT_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + ptr += n; \ + break; + +#define CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) \ + case BT_NONASCII: \ + if (!IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; \ + } \ + case BT_NMSTRT: \ + case BT_HEX: \ + ptr += MINBPC(enc); \ + break; \ + CHECK_NMSTRT_CASE(2, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(3, enc, ptr, end, nextTokPtr) \ + CHECK_NMSTRT_CASE(4, enc, ptr, end, nextTokPtr) + +#ifndef PREFIX +#define PREFIX(ident) ident +#endif + +/* ptr points to character following " */ + switch (BYTE_TYPE(enc, ptr + MINBPC(enc))) { + case BT_S: case BT_CR: case BT_LF: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + /* fall through */ + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DECL_OPEN; + case BT_NMSTRT: + case BT_HEX: + ptr += MINBPC(enc); + break; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(checkPiTarget)(const ENCODING *enc, const char *ptr, + const char *end, int *tokPtr) +{ + int upper = 0; + *tokPtr = XML_TOK_PI; + if (end - ptr != MINBPC(enc)*3) + return 1; + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_x: + break; + case ASCII_X: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_m: + break; + case ASCII_M: + upper = 1; + break; + default: + return 1; + } + ptr += MINBPC(enc); + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + break; + case ASCII_L: + upper = 1; + break; + default: + return 1; + } + if (upper) + return 0; + *tokPtr = XML_TOK_XML_DECL; + return 1; +} + +/* ptr points to character following " 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CDATA_SECT_CLOSE; + case BT_CR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + case BT_RSQB: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following " 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_LT: + return PREFIX(scanLt)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_AMP: + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_CR: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + case BT_LF: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_RSQB)) + break; + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_RSQB; + if (!CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr -= MINBPC(enc); + break; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + INVALID_CASES(ptr, nextTokPtr) + default: + ptr += MINBPC(enc); + break; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n || IS_INVALID_CHAR(enc, ptr, n)) { \ + *nextTokPtr = ptr; \ + return XML_TOK_DATA_CHARS; \ + } \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_RSQB: + if (ptr + MINBPC(enc) != end) { + if (!CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_RSQB)) { + ptr += MINBPC(enc); + break; + } + if (ptr + 2*MINBPC(enc) != end) { + if (!CHAR_MATCHES(enc, ptr + 2*MINBPC(enc), ASCII_GT)) { + ptr += MINBPC(enc); + break; + } + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_INVALID; + } + } + /* fall through */ + case BT_AMP: + case BT_LT: + case BT_NONXML: + case BT_MALFORM: + case BT_TRAIL: + case BT_CR: + case BT_LF: + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +/* ptr points to character following "%" */ + +static int PTRCALL +PREFIX(scanPercent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + case BT_S: case BT_LF: case BT_CR: case BT_PERCNT: + *nextTokPtr = ptr; + return XML_TOK_PERCENT; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_SEMI: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_PARAM_ENTITY_REF; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(scanPoundName)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NMSTRT_CASES(enc, ptr, end, nextTokPtr) + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_CR: case BT_LF: case BT_S: + case BT_RPAR: case BT_GT: case BT_PERCNT: case BT_VERBAR: + *nextTokPtr = ptr; + return XML_TOK_POUND_NAME; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -XML_TOK_POUND_NAME; +} + +static int PTRCALL +PREFIX(scanLit)(int open, const ENCODING *enc, + const char *ptr, const char *end, + const char **nextTokPtr) +{ + while (ptr != end) { + int t = BYTE_TYPE(enc, ptr); + switch (t) { + INVALID_CASES(ptr, nextTokPtr) + case BT_QUOT: + case BT_APOS: + ptr += MINBPC(enc); + if (t != open) + break; + if (ptr == end) + return -XML_TOK_LITERAL; + *nextTokPtr = ptr; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_CR: case BT_LF: + case BT_GT: case BT_PERCNT: case BT_LSQB: + return XML_TOK_LITERAL; + default: + return XML_TOK_INVALID; + } + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +static int PTRCALL +PREFIX(prologTok)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + int tok; + if (ptr == end) + return XML_TOK_NONE; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + if (n == 0) + return XML_TOK_PARTIAL; + end = ptr + n; + } + } + switch (BYTE_TYPE(enc, ptr)) { + case BT_QUOT: + return PREFIX(scanLit)(BT_QUOT, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_APOS: + return PREFIX(scanLit)(BT_APOS, enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_LT: + { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_PARTIAL; + switch (BYTE_TYPE(enc, ptr)) { + case BT_EXCL: + return PREFIX(scanDecl)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_QUEST: + return PREFIX(scanPi)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_NMSTRT: + case BT_HEX: + case BT_NONASCII: + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + *nextTokPtr = ptr - MINBPC(enc); + return XML_TOK_INSTANCE_START; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + case BT_CR: + if (ptr + MINBPC(enc) == end) { + *nextTokPtr = end; + /* indicate that this might be part of a CR/LF pair */ + return -XML_TOK_PROLOG_S; + } + /* fall through */ + case BT_S: case BT_LF: + for (;;) { + ptr += MINBPC(enc); + if (ptr == end) + break; + switch (BYTE_TYPE(enc, ptr)) { + case BT_S: case BT_LF: + break; + case BT_CR: + /* don't split CR/LF pair */ + if (ptr + MINBPC(enc) != end) + break; + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + } + } + *nextTokPtr = ptr; + return XML_TOK_PROLOG_S; + case BT_PERCNT: + return PREFIX(scanPercent)(enc, ptr + MINBPC(enc), end, nextTokPtr); + case BT_COMMA: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_COMMA; + case BT_LSQB: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_BRACKET; + case BT_RSQB: + ptr += MINBPC(enc); + if (ptr == end) + return -XML_TOK_CLOSE_BRACKET; + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + if (ptr + MINBPC(enc) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_GT)) { + *nextTokPtr = ptr + 2*MINBPC(enc); + return XML_TOK_COND_SECT_CLOSE; + } + } + *nextTokPtr = ptr; + return XML_TOK_CLOSE_BRACKET; + case BT_LPAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OPEN_PAREN; + case BT_RPAR: + ptr += MINBPC(enc); + if (ptr == end) + return -XML_TOK_CLOSE_PAREN; + switch (BYTE_TYPE(enc, ptr)) { + case BT_AST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_ASTERISK; + case BT_QUEST: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_QUESTION; + case BT_PLUS: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_CLOSE_PAREN_PLUS; + case BT_CR: case BT_LF: case BT_S: + case BT_GT: case BT_COMMA: case BT_VERBAR: + case BT_RPAR: + *nextTokPtr = ptr; + return XML_TOK_CLOSE_PAREN; + } + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_VERBAR: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_OR; + case BT_GT: + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DECL_CLOSE; + case BT_NUM: + return PREFIX(scanPoundName)(enc, ptr + MINBPC(enc), end, nextTokPtr); +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (end - ptr < n) \ + return XML_TOK_PARTIAL_CHAR; \ + if (IS_NMSTRT_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NAME; \ + break; \ + } \ + if (IS_NAME_CHAR(enc, ptr, n)) { \ + ptr += n; \ + tok = XML_TOK_NMTOKEN; \ + break; \ + } \ + *nextTokPtr = ptr; \ + return XML_TOK_INVALID; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NMSTRT: + case BT_HEX: + tok = XML_TOK_NAME; + ptr += MINBPC(enc); + break; + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: +#ifdef XML_NS + case BT_COLON: +#endif + tok = XML_TOK_NMTOKEN; + ptr += MINBPC(enc); + break; + case BT_NONASCII: + if (IS_NMSTRT_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NAME; + break; + } + if (IS_NAME_CHAR_MINBPC(enc, ptr)) { + ptr += MINBPC(enc); + tok = XML_TOK_NMTOKEN; + break; + } + /* fall through */ + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + case BT_GT: case BT_RPAR: case BT_COMMA: + case BT_VERBAR: case BT_LSQB: case BT_PERCNT: + case BT_S: case BT_CR: case BT_LF: + *nextTokPtr = ptr; + return tok; +#ifdef XML_NS + case BT_COLON: + ptr += MINBPC(enc); + switch (tok) { + case XML_TOK_NAME: + if (ptr == end) + return XML_TOK_PARTIAL; + tok = XML_TOK_PREFIXED_NAME; + switch (BYTE_TYPE(enc, ptr)) { + CHECK_NAME_CASES(enc, ptr, end, nextTokPtr) + default: + tok = XML_TOK_NMTOKEN; + break; + } + break; + case XML_TOK_PREFIXED_NAME: + tok = XML_TOK_NMTOKEN; + break; + } + break; +#endif + case BT_PLUS: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_PLUS; + case BT_AST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_ASTERISK; + case BT_QUEST: + if (tok == XML_TOK_NMTOKEN) { + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_NAME_QUESTION; + default: + *nextTokPtr = ptr; + return XML_TOK_INVALID; + } + } + return -tok; +} + +static int PTRCALL +PREFIX(attributeValueTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LT: + /* this is for inside entity references */ + *nextTokPtr = ptr; + return XML_TOK_INVALID; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_S: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_ATTRIBUTE_VALUE_S; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +static int PTRCALL +PREFIX(entityValueTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + const char *start; + if (ptr == end) + return XML_TOK_NONE; + start = ptr; + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_AMP: + if (ptr == start) + return PREFIX(scanRef)(enc, ptr + MINBPC(enc), end, nextTokPtr); + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_PERCNT: + if (ptr == start) { + int tok = PREFIX(scanPercent)(enc, ptr + MINBPC(enc), + end, nextTokPtr); + return (tok == XML_TOK_PERCENT) ? XML_TOK_INVALID : tok; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_LF: + if (ptr == start) { + *nextTokPtr = ptr + MINBPC(enc); + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + case BT_CR: + if (ptr == start) { + ptr += MINBPC(enc); + if (ptr == end) + return XML_TOK_TRAILING_CR; + if (BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + *nextTokPtr = ptr; + return XML_TOK_DATA_NEWLINE; + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; + default: + ptr += MINBPC(enc); + break; + } + } + *nextTokPtr = ptr; + return XML_TOK_DATA_CHARS; +} + +#ifdef XML_DTD + +static int PTRCALL +PREFIX(ignoreSectionTok)(const ENCODING *enc, const char *ptr, + const char *end, const char **nextTokPtr) +{ + int level = 0; + if (MINBPC(enc) > 1) { + size_t n = end - ptr; + if (n & (MINBPC(enc) - 1)) { + n &= ~(MINBPC(enc) - 1); + end = ptr + n; + } + } + while (ptr != end) { + switch (BYTE_TYPE(enc, ptr)) { + INVALID_CASES(ptr, nextTokPtr) + case BT_LT: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_EXCL)) { + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_LSQB)) { + ++level; + ptr += MINBPC(enc); + } + } + break; + case BT_RSQB: + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_RSQB)) { + if ((ptr += MINBPC(enc)) == end) + return XML_TOK_PARTIAL; + if (CHAR_MATCHES(enc, ptr, ASCII_GT)) { + ptr += MINBPC(enc); + if (level == 0) { + *nextTokPtr = ptr; + return XML_TOK_IGNORE_SECT; + } + --level; + } + } + break; + default: + ptr += MINBPC(enc); + break; + } + } + return XML_TOK_PARTIAL; +} + +#endif /* XML_DTD */ + +static int PTRCALL +PREFIX(isPublicId)(const ENCODING *enc, const char *ptr, const char *end, + const char **badPtr) +{ + ptr += MINBPC(enc); + end -= MINBPC(enc); + for (; ptr != end; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_DIGIT: + case BT_HEX: + case BT_MINUS: + case BT_APOS: + case BT_LPAR: + case BT_RPAR: + case BT_PLUS: + case BT_COMMA: + case BT_SOL: + case BT_EQUALS: + case BT_QUEST: + case BT_CR: + case BT_LF: + case BT_SEMI: + case BT_EXCL: + case BT_AST: + case BT_PERCNT: + case BT_NUM: +#ifdef XML_NS + case BT_COLON: +#endif + break; + case BT_S: + if (CHAR_MATCHES(enc, ptr, ASCII_TAB)) { + *badPtr = ptr; + return 0; + } + break; + case BT_NAME: + case BT_NMSTRT: + if (!(BYTE_TO_ASCII(enc, ptr) & ~0x7f)) + break; + default: + switch (BYTE_TO_ASCII(enc, ptr)) { + case 0x24: /* $ */ + case 0x40: /* @ */ + break; + default: + *badPtr = ptr; + return 0; + } + break; + } + } + return 1; +} + +/* This must only be called for a well-formed start-tag or empty + element tag. Returns the number of attributes. Pointers to the + first attsMax attributes are stored in atts. +*/ + +static int PTRCALL +PREFIX(getAtts)(const ENCODING *enc, const char *ptr, + int attsMax, ATTRIBUTE *atts) +{ + enum { other, inName, inValue } state = inName; + int nAtts = 0; + int open = 0; /* defined when state == inValue; + initialization just to shut up compilers */ + + for (ptr += MINBPC(enc);; ptr += MINBPC(enc)) { + switch (BYTE_TYPE(enc, ptr)) { +#define START_NAME \ + if (state == other) { \ + if (nAtts < attsMax) { \ + atts[nAtts].name = ptr; \ + atts[nAtts].normalized = 1; \ + } \ + state = inName; \ + } +#define LEAD_CASE(n) \ + case BT_LEAD ## n: START_NAME ptr += (n - MINBPC(enc)); break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: + case BT_HEX: + START_NAME + break; +#undef START_NAME + case BT_QUOT: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_QUOT; + } + else if (open == BT_QUOT) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_APOS: + if (state != inValue) { + if (nAtts < attsMax) + atts[nAtts].valuePtr = ptr + MINBPC(enc); + state = inValue; + open = BT_APOS; + } + else if (open == BT_APOS) { + state = other; + if (nAtts < attsMax) + atts[nAtts].valueEnd = ptr; + nAtts++; + } + break; + case BT_AMP: + if (nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_S: + if (state == inName) + state = other; + else if (state == inValue + && nAtts < attsMax + && atts[nAtts].normalized + && (ptr == atts[nAtts].valuePtr + || BYTE_TO_ASCII(enc, ptr) != ASCII_SPACE + || BYTE_TO_ASCII(enc, ptr + MINBPC(enc)) == ASCII_SPACE + || BYTE_TYPE(enc, ptr + MINBPC(enc)) == open)) + atts[nAtts].normalized = 0; + break; + case BT_CR: case BT_LF: + /* This case ensures that the first attribute name is counted + Apart from that we could just change state on the quote. */ + if (state == inName) + state = other; + else if (state == inValue && nAtts < attsMax) + atts[nAtts].normalized = 0; + break; + case BT_GT: + case BT_SOL: + if (state != inValue) + return nAtts; + break; + default: + break; + } + } + /* not reached */ +} + +static int PTRFASTCALL +PREFIX(charRefNumber)(const ENCODING *enc, const char *ptr) +{ + int result = 0; + /* skip &# */ + ptr += 2*MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_x)) { + for (ptr += MINBPC(enc); + !CHAR_MATCHES(enc, ptr, ASCII_SEMI); + ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + switch (c) { + case ASCII_0: case ASCII_1: case ASCII_2: case ASCII_3: case ASCII_4: + case ASCII_5: case ASCII_6: case ASCII_7: case ASCII_8: case ASCII_9: + result <<= 4; + result |= (c - ASCII_0); + break; + case ASCII_A: case ASCII_B: case ASCII_C: + case ASCII_D: case ASCII_E: case ASCII_F: + result <<= 4; + result += 10 + (c - ASCII_A); + break; + case ASCII_a: case ASCII_b: case ASCII_c: + case ASCII_d: case ASCII_e: case ASCII_f: + result <<= 4; + result += 10 + (c - ASCII_a); + break; + } + if (result >= 0x110000) + return -1; + } + } + else { + for (; !CHAR_MATCHES(enc, ptr, ASCII_SEMI); ptr += MINBPC(enc)) { + int c = BYTE_TO_ASCII(enc, ptr); + result *= 10; + result += (c - ASCII_0); + if (result >= 0x110000) + return -1; + } + } + return checkCharRefNumber(result); +} + +static int PTRCALL +PREFIX(predefinedEntityName)(const ENCODING *enc, const char *ptr, + const char *end) +{ + switch ((end - ptr)/MINBPC(enc)) { + case 2: + if (CHAR_MATCHES(enc, ptr + MINBPC(enc), ASCII_t)) { + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_l: + return ASCII_LT; + case ASCII_g: + return ASCII_GT; + } + } + break; + case 3: + if (CHAR_MATCHES(enc, ptr, ASCII_a)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_m)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) + return ASCII_AMP; + } + } + break; + case 4: + switch (BYTE_TO_ASCII(enc, ptr)) { + case ASCII_q: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_u)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_t)) + return ASCII_QUOT; + } + } + break; + case ASCII_a: + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_p)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_o)) { + ptr += MINBPC(enc); + if (CHAR_MATCHES(enc, ptr, ASCII_s)) + return ASCII_APOS; + } + } + break; + } + } + return 0; +} + +static int PTRCALL +PREFIX(sameName)(const ENCODING *enc, const char *ptr1, const char *ptr2) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr1)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + if (*ptr1++ != *ptr2++) \ + return 0; + LEAD_CASE(4) LEAD_CASE(3) LEAD_CASE(2) +#undef LEAD_CASE + /* fall through */ + if (*ptr1++ != *ptr2++) + return 0; + break; + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 1) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 2) { + if (*ptr2++ != *ptr1++) + return 0; + if (MINBPC(enc) > 3) { + if (*ptr2++ != *ptr1++) + return 0; + } + } + } + break; + default: + if (MINBPC(enc) == 1 && *ptr1 == *ptr2) + return 1; + switch (BYTE_TYPE(enc, ptr2)) { + case BT_LEAD2: + case BT_LEAD3: + case BT_LEAD4: + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + return 0; + default: + return 1; + } + } + } + /* not reached */ +} + +static int PTRCALL +PREFIX(nameMatchesAscii)(const ENCODING *enc, const char *ptr1, + const char *end1, const char *ptr2) +{ + for (; *ptr2; ptr1 += MINBPC(enc), ptr2++) { + if (ptr1 == end1) + return 0; + if (!CHAR_MATCHES(enc, ptr1, *ptr2)) + return 0; + } + return ptr1 == end1; +} + +static int PTRFASTCALL +PREFIX(nameLength)(const ENCODING *enc, const char *ptr) +{ + const char *start = ptr; + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: ptr += n; break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_NONASCII: + case BT_NMSTRT: +#ifdef XML_NS + case BT_COLON: +#endif + case BT_HEX: + case BT_DIGIT: + case BT_NAME: + case BT_MINUS: + ptr += MINBPC(enc); + break; + default: + return (int)(ptr - start); + } + } +} + +static const char * PTRFASTCALL +PREFIX(skipS)(const ENCODING *enc, const char *ptr) +{ + for (;;) { + switch (BYTE_TYPE(enc, ptr)) { + case BT_LF: + case BT_CR: + case BT_S: + ptr += MINBPC(enc); + break; + default: + return ptr; + } + } +} + +static void PTRCALL +PREFIX(updatePosition)(const ENCODING *enc, + const char *ptr, + const char *end, + POSITION *pos) +{ + while (ptr < end) { + switch (BYTE_TYPE(enc, ptr)) { +#define LEAD_CASE(n) \ + case BT_LEAD ## n: \ + ptr += n; \ + break; + LEAD_CASE(2) LEAD_CASE(3) LEAD_CASE(4) +#undef LEAD_CASE + case BT_LF: + pos->columnNumber = (XML_Size)-1; + pos->lineNumber++; + ptr += MINBPC(enc); + break; + case BT_CR: + pos->lineNumber++; + ptr += MINBPC(enc); + if (ptr != end && BYTE_TYPE(enc, ptr) == BT_LF) + ptr += MINBPC(enc); + pos->columnNumber = (XML_Size)-1; + break; + default: + ptr += MINBPC(enc); + break; + } + pos->columnNumber++; + } +} + +#undef DO_LEAD_CASE +#undef MULTIBYTE_CASES +#undef INVALID_CASES +#undef CHECK_NAME_CASE +#undef CHECK_NAME_CASES +#undef CHECK_NMSTRT_CASE +#undef CHECK_NMSTRT_CASES + +#endif /* XML_TOK_IMPL_C */ diff --git a/source/lib/expat_lib/xmltok_impl.h b/source/lib/expat_lib/xmltok_impl.h new file mode 100644 index 0000000..da0ea60 --- /dev/null +++ b/source/lib/expat_lib/xmltok_impl.h @@ -0,0 +1,46 @@ +/* +Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd +See the file COPYING for copying permission. +*/ + +enum { + BT_NONXML, + BT_MALFORM, + BT_LT, + BT_AMP, + BT_RSQB, + BT_LEAD2, + BT_LEAD3, + BT_LEAD4, + BT_TRAIL, + BT_CR, + BT_LF, + BT_GT, + BT_QUOT, + BT_APOS, + BT_EQUALS, + BT_QUEST, + BT_EXCL, + BT_SOL, + BT_SEMI, + BT_NUM, + BT_LSQB, + BT_S, + BT_NMSTRT, + BT_COLON, + BT_HEX, + BT_DIGIT, + BT_NAME, + BT_MINUS, + BT_OTHER, /* known not to be a name or name start character */ + BT_NONASCII, /* might be a name or name start character */ + BT_PERCNT, + BT_LPAR, + BT_RPAR, + BT_AST, + BT_PLUS, + BT_COMMA, + BT_VERBAR +}; + +#include diff --git a/source/lib/expat_lib/xmltok_ns.c b/source/lib/expat_lib/xmltok_ns.c new file mode 100644 index 0000000..c3b88fd --- /dev/null +++ b/source/lib/expat_lib/xmltok_ns.c @@ -0,0 +1,115 @@ +/* Copyright (c) 1998, 1999 Thai Open Source Software Center Ltd + See the file COPYING for copying permission. +*/ + +/* This file is included! */ +#ifdef XML_TOK_NS_C + +const ENCODING * +NS(XmlGetUtf8InternalEncoding)(void) +{ + return &ns(internal_utf8_encoding).enc; +} + +const ENCODING * +NS(XmlGetUtf16InternalEncoding)(void) +{ +#if BYTEORDER == 1234 + return &ns(internal_little2_encoding).enc; +#elif BYTEORDER == 4321 + return &ns(internal_big2_encoding).enc; +#else + const short n = 1; + return (*(const char *)&n + ? &ns(internal_little2_encoding).enc + : &ns(internal_big2_encoding).enc); +#endif +} + +static const ENCODING * const NS(encodings)[] = { + &ns(latin1_encoding).enc, + &ns(ascii_encoding).enc, + &ns(utf8_encoding).enc, + &ns(big2_encoding).enc, + &ns(big2_encoding).enc, + &ns(little2_encoding).enc, + &ns(utf8_encoding).enc /* NO_ENC */ +}; + +static int PTRCALL +NS(initScanProlog)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, + XML_PROLOG_STATE, ptr, end, nextTokPtr); +} + +static int PTRCALL +NS(initScanContent)(const ENCODING *enc, const char *ptr, const char *end, + const char **nextTokPtr) +{ + return initScan(NS(encodings), (const INIT_ENCODING *)enc, + XML_CONTENT_STATE, ptr, end, nextTokPtr); +} + +int +NS(XmlInitEncoding)(INIT_ENCODING *p, const ENCODING **encPtr, + const char *name) +{ + int i = getEncodingIndex(name); + if (i == UNKNOWN_ENC) + return 0; + SET_INIT_ENC_INDEX(p, i); + p->initEnc.scanners[XML_PROLOG_STATE] = NS(initScanProlog); + p->initEnc.scanners[XML_CONTENT_STATE] = NS(initScanContent); + p->initEnc.updatePosition = initUpdatePosition; + p->encPtr = encPtr; + *encPtr = &(p->initEnc); + return 1; +} + +static const ENCODING * +NS(findEncoding)(const ENCODING *enc, const char *ptr, const char *end) +{ +#define ENCODING_MAX 128 + char buf[ENCODING_MAX]; + char *p = buf; + int i; + XmlUtf8Convert(enc, &ptr, end, &p, p + ENCODING_MAX - 1); + if (ptr != end) + return 0; + *p = 0; + if (streqci(buf, KW_UTF_16) && enc->minBytesPerChar == 2) + return enc; + i = getEncodingIndex(buf); + if (i == UNKNOWN_ENC) + return 0; + return NS(encodings)[i]; +} + +int +NS(XmlParseXmlDecl)(int isGeneralTextEntity, + const ENCODING *enc, + const char *ptr, + const char *end, + const char **badPtr, + const char **versionPtr, + const char **versionEndPtr, + const char **encodingName, + const ENCODING **encoding, + int *standalone) +{ + return doParseXmlDecl(NS(findEncoding), + isGeneralTextEntity, + enc, + ptr, + end, + badPtr, + versionPtr, + versionEndPtr, + encodingName, + encoding, + standalone); +} + +#endif /* XML_TOK_NS_C */ diff --git a/source/lib/gpr_sdk/CMakeLists.txt b/source/lib/gpr_sdk/CMakeLists.txt new file mode 100644 index 0000000..9ce99c4 --- /dev/null +++ b/source/lib/gpr_sdk/CMakeLists.txt @@ -0,0 +1,44 @@ +# library +set( LIB_NAME gpr_sdk ) + +# get source files +file( GLOB SRC_FILES "private/*.cpp" "public/*.cpp" ) + +# get include files +file( GLOB INC_FILES "private/*.h" "public/*.h" ) + +# add include files from other folders +include_directories( "./public" ) +include_directories( "../common/private" ) +include_directories( "../common/public" ) +include_directories( "../dng_sdk" ) +include_directories( "../vc5_common" ) + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + include_directories( "../vc5_decoder" ) + add_definitions("-DGPR_READING=1") +else(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + add_definitions("-DGPR_READING=0") +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_decoder") + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + include_directories( "../vc5_encoder" ) + add_definitions("-DGPR_WRITING=1") +else(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + add_definitions("-DGPR_WRITING=0") +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/vc5_encoder") + +if(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + include_directories( "../tiny_jpeg" ) + add_definitions("-DGPR_JPEG_AVAILABLE=1") +else(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + add_definitions("-DGPR_JPEG_AVAILABLE=0") +endif(EXISTS "${CMAKE_SOURCE_DIR}/source/lib/tiny_jpeg") + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/source/lib/gpr_sdk/private/gpr.cpp b/source/lib/gpr_sdk/private/gpr.cpp new file mode 100755 index 0000000..516c0f5 --- /dev/null +++ b/source/lib/gpr_sdk/private/gpr.cpp @@ -0,0 +1,1849 @@ +/*! @file gpr.c + * + * @brief Implementation of top level functions that implement GPR-SDK API + * + * GPR API can be invoked by simply including this header file. + * This file includes all other header files that are needed. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "gpr.h" + +#include "dng_camera_profile.h" +#include "dng_color_space.h" +#include "dng_date_time.h" +#include "dng_exceptions.h" +#include "dng_file_stream.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_ifd.h" +#include "dng_image_writer.h" +#include "dng_info.h" +#include "dng_linearization_info.h" +#include "dng_mosaic_info.h" +#include "dng_negative.h" +#include "dng_preview.h" +#include "dng_render.h" +#include "dng_simple_image.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_xmp.h" +#include "dng_xmp_sdk.h" +#include "dng_memory_stream.h" +#include "dng_bottlenecks.h" + +#include "dng_misc_opcodes.h" +#include "dng_gain_map.h" +#include "dng_lens_correction.h" + +#include "gpr_utils.h" + +#include "macros.h" +#include "gpr_buffer.h" +#include "gpr_buffer_auto.h" + +#if GPR_READING +#include "vc5_decoder.h" +#endif + +#if GPR_WRITING +#include "gpr_image_writer.h" +#endif + +#if GPR_READING +#include "gpr_read_image.h" +#endif + +#if GPR_JPEG_AVAILABLE +#include "jpeg.h" +#endif + +extern bool gDNGShowTimers; + +#define PRINT_MATRIX 0 + +// Include logging file (for reporting timing) +#include "log.h" + +void find_rational(float number, float error_tolerance, int* numerator, int* denominator_pow2) +{ + int _num; + int _den_pow2; + + _den_pow2 = 1; + + while(1) + { + _num = number * (1 << _den_pow2); + + float error = number - (float)_num / (float)((1 << _den_pow2)); + + if( error < error_tolerance ) + break; + + _den_pow2++; + } + + *numerator = _num; + *denominator_pow2 = _den_pow2; +} + +static void unpack_pixel_format( const gpr_buffer_auto* input_buffer, const gpr_parameters* convert_params, gpr_buffer_auto* output_buffer ) +{ + size_t buffer_size = convert_params->input_height * convert_params->input_width * 2; + output_buffer->allocate( buffer_size ); + + unsigned char* src = input_buffer->to_uchar(); + uint16_t* dst = output_buffer->to_uint16_t(); + + int src_stride = convert_params->input_pitch; + int dst_stride = convert_params->input_width; + + for (unsigned int row = 0; row < convert_params->input_height; row++ ) + { + for (unsigned int col = 0; col < (convert_params->input_width / 2); col++ ) + { + unsigned char byte_0 = *(src + col * 3 + 0); + unsigned char byte_1 = *(src + col * 3 + 1); + unsigned char byte_2 = *(src + col * 3 + 2); + + uint16_t pix1 = (byte_2 << 4) + ((byte_1 & 0xf0) >> 4); + uint16_t pix2 = (byte_0) + ((byte_1 & 0x0f) << 8); + + *(dst + col * 2 + 0) = pix2; + *(dst + col * 2 + 1) = pix1; + } + + src += src_stride; + dst += dst_stride; + } +} + +#if GPR_WRITING +static void set_vc5_encoder_parameters( vc5_encoder_parameters& vc5_encoder_params, const gpr_parameters* convert_params ) +{ + vc5_encoder_params.input_width = convert_params->input_width; + vc5_encoder_params.input_height = convert_params->input_height; + vc5_encoder_params.input_pitch = convert_params->input_pitch; + + switch ( convert_params->tuning_info.pixel_format ) + { + case PIXEL_FORMAT_RGGB_12: + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_RGGB_12; + break; + + case PIXEL_FORMAT_RGGB_12P: + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_RGGB_12P; + break; + + case PIXEL_FORMAT_RGGB_14: + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_RGGB_14; + break; + + case PIXEL_FORMAT_GBRG_12: + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_GBRG_12; + break; + + case PIXEL_FORMAT_GBRG_12P: + vc5_encoder_params.pixel_format = VC5_ENCODER_PIXEL_FORMAT_GBRG_12P; + break; + + default: + break; + } + + if( convert_params->fast_encoding ) + vc5_encoder_params.quality_setting = VC5_ENCODER_QUALITY_SETTING_MEDIUM; + else + vc5_encoder_params.quality_setting = VC5_ENCODER_QUALITY_SETTING_FS1; +} +#endif + +void gpr_parameters_set_defaults(gpr_parameters* x) +{ + memset(x, 0, sizeof(gpr_parameters)); + + gpr_exif_info_set_defaults(&x->exif_info); + gpr_profile_info_set_defaults(&x->profile_info); + gpr_tuning_info_set_defaults(&x->tuning_info); + + x->enable_preview = true; + + x->compute_md5sum = false; + + x->fast_encoding = false; +} + +void gpr_parameters_construct_copy(const gpr_parameters* y, gpr_parameters* x, gpr_malloc mem_alloc) +{ + gpr_parameters_set_defaults(x); + + *x = *y; + + if( y->gpmf_payload.size > 0 && y->gpmf_payload.buffer != NULL ) + { + x->gpmf_payload.buffer = mem_alloc( y->gpmf_payload.size ); + memcpy( x->gpmf_payload.buffer, y->gpmf_payload.buffer, y->gpmf_payload.size ); + } + + if( y->tuning_info.gain_map.size > 0 ) + { + for ( int i = 0; i < 4; i++) + { + if( y->tuning_info.gain_map.buffers[i] != 0 ) + { + x->tuning_info.gain_map.buffers[i] = (char*)mem_alloc( y->tuning_info.gain_map.size ); + memcpy( x->tuning_info.gain_map.buffers[i], y->tuning_info.gain_map.buffers[i], y->tuning_info.gain_map.size ); + } + } + } +} + +void gpr_parameters_destroy(gpr_parameters* x, gpr_free mem_free) +{ + if( x->gpmf_payload.buffer && x->gpmf_payload.size > 0 ) + { + mem_free( x->gpmf_payload.buffer ); + } + + if( x->tuning_info.gain_map.size > 0 ) + { + for ( int i = 0; i < 4; i++) + { + if( x->tuning_info.gain_map.buffers[i] ) + mem_free( x->tuning_info.gain_map.buffers[i] ); + } + } +} + + +static dng_srational convert_to_dng_srational(gpr_signed_rational x) +{ + return dng_srational(x.numerator, x.denominator); +} + +static dng_urational convert_to_dng_urational(gpr_unsigned_rational x) +{ + return dng_urational(x.numerator, x.denominator); +} + +static gpr_unsigned_rational convert_to_unsigned_rational(dng_urational x) +{ + gpr_unsigned_rational a; + a.numerator = x.n; + a.denominator = x.d; + + return a; +} + +static gpr_signed_rational convert_to_signed_rational(dng_srational x) +{ + gpr_signed_rational a; + a.numerator = x.n; + a.denominator = x.d; + + return a; +} + +static dng_date_time convert_to_dng_date_time( const gpr_date_and_time& x ) +{ + return dng_date_time (x.year, x.month, x.day, x.hour, x.minute, x.second ); +} + +static gpr_date_and_time convert_to_dng_date_and_time( const dng_date_time& x ) +{ + gpr_date_and_time a; + + a.year = x.fYear; + a.month = x.fMonth; + a.day = x.fDay; + a.hour = x.fHour; + a.minute = x.fMinute; + a.second = x.fSecond; + + return a; +} + +static void convert_dng_exif_info_to_dng_exif( dng_exif* dst_exif, const gpr_exif_info* src_exif ) +{ + dst_exif->fModel.Set_ASCII( src_exif->camera_model ); + dst_exif->fMake.Set_ASCII( src_exif->camera_make ); + dst_exif->fCameraSerialNumber.Set_ASCII( src_exif->camera_serial ); + dst_exif->fImageDescription.Set_ASCII( src_exif->image_description ); + + dst_exif->fApertureValue = convert_to_dng_urational( src_exif->aperture ); + dst_exif->fMaxApertureValue = convert_to_dng_urational( src_exif->aperture ); + dst_exif->fFNumber = convert_to_dng_urational( src_exif->f_stop_number ); + dst_exif->fExposureTime = convert_to_dng_urational( src_exif->exposure_time ); + dst_exif->fFocalLength = convert_to_dng_urational( src_exif->focal_length ); + dst_exif->fDigitalZoomRatio = convert_to_dng_urational( src_exif->digital_zoom ); + dst_exif->fExposureBiasValue = convert_to_dng_srational( src_exif->exposure_bias ); + + const int32_t focal_plane = 72; + dst_exif->fFocalPlaneXResolution = dng_urational(focal_plane, 1); + dst_exif->fFocalPlaneYResolution = dng_urational(focal_plane, 1); + + dst_exif->fMeteringMode = src_exif->metering_mode; + dst_exif->fFocalLengthIn35mmFilm = src_exif->focal_length_in_35mm_film; + dst_exif->fExposureProgram = src_exif->exposure_program; + dst_exif->fLightSource = src_exif->light_source; + dst_exif->fFlash = src_exif->flash; + dst_exif->fSensingMethod = src_exif->sensing_method; + dst_exif->fFileSource = src_exif->file_source; + dst_exif->fSceneType = src_exif->scene_type; + dst_exif->fWhiteBalance = src_exif->white_balance; + dst_exif->fExposureMode = src_exif->exposure_mode; + dst_exif->fSceneCaptureType = src_exif->scene_capture_type; + dst_exif->fGainControl = src_exif->gain_control; + dst_exif->fContrast = src_exif->contrast; + dst_exif->fSaturation = src_exif->saturation; + dst_exif->fSharpness = src_exif->sharpness; + + dst_exif->fISOSpeedRatings[0] = src_exif->iso_speed_rating; + + dst_exif->fComponentsConfiguration = 0x04050600; //?? - since we're raw? + + dst_exif->fDateTimeOriginal.SetDateTime( convert_to_dng_date_time( src_exif->date_time_original ) ); + dst_exif->fDateTimeDigitized.SetDateTime( convert_to_dng_date_time( src_exif->date_time_digitized ) ); + + dst_exif->fSoftware.Set(src_exif->software_version); + + dst_exif->fUserComment.Set(src_exif->user_comment); + + const gpr_gps_info& src_gps_info = src_exif->gps_info; + + if( src_gps_info.gps_info_valid ) + { + dst_exif->fGPSVersionID = src_gps_info.version_id; + + dst_exif->fGPSLatitudeRef.Set( src_gps_info.latitude_ref ); + + dst_exif->fGPSLatitude[0] = convert_to_dng_urational( src_gps_info.latitude[0] ); + dst_exif->fGPSLatitude[1] = convert_to_dng_urational( src_gps_info.latitude[1] ); + dst_exif->fGPSLatitude[2] = convert_to_dng_urational( src_gps_info.latitude[2] ); + + dst_exif->fGPSLongitudeRef.Set( src_gps_info.longitude_ref ); + + dst_exif->fGPSLongitude[0] = convert_to_dng_urational( src_gps_info.longitude[0] ); + dst_exif->fGPSLongitude[1] = convert_to_dng_urational( src_gps_info.longitude[1] ); + dst_exif->fGPSLongitude[2] = convert_to_dng_urational( src_gps_info.longitude[2] ); + + dst_exif->fGPSAltitudeRef = src_gps_info.altitude_ref; + + dst_exif->fGPSAltitude = convert_to_dng_urational( src_gps_info.altitude ); + + dst_exif->fGPSTimeStamp[0] = convert_to_dng_urational( src_gps_info.time_stamp[0] ); + dst_exif->fGPSTimeStamp[1] = convert_to_dng_urational( src_gps_info.time_stamp[1] ); + dst_exif->fGPSTimeStamp[2] = convert_to_dng_urational( src_gps_info.time_stamp[2] ); + + dst_exif->fGPSSatellites.Set( src_gps_info.satellites ); + + dst_exif->fGPSStatus.Set( src_gps_info.status ); + + dst_exif->fGPSMeasureMode.Set( src_gps_info.measure_mode ); + + dst_exif->fGPSDOP = convert_to_dng_urational( src_gps_info.dop ); + + dst_exif->fGPSSpeedRef.Set( src_gps_info.speed_ref ); + + dst_exif->fGPSSpeed = convert_to_dng_urational( src_gps_info.speed ); + + dst_exif->fGPSTrackRef.Set( src_gps_info.track_ref ); + + dst_exif->fGPSTrack = convert_to_dng_urational( src_gps_info.track ); + + dst_exif->fGPSImgDirectionRef.Set( src_gps_info.img_direction_ref ); + + dst_exif->fGPSImgDirection = convert_to_dng_urational( src_gps_info.img_direction ); + + dst_exif->fGPSMapDatum.Set( src_gps_info.map_datum ); + + dst_exif->fGPSDestLatitudeRef.Set( src_gps_info.dest_latitude_ref ); + + dst_exif->fGPSDestLatitude[0] = convert_to_dng_urational( src_gps_info.dest_latitude[0] ); + dst_exif->fGPSDestLatitude[1] = convert_to_dng_urational( src_gps_info.dest_latitude[1] ); + dst_exif->fGPSDestLatitude[2] = convert_to_dng_urational( src_gps_info.dest_latitude[2] ); + + dst_exif->fGPSDestLongitudeRef.Set( src_gps_info.dest_longitude_ref ); + + dst_exif->fGPSDestLongitude[0] = convert_to_dng_urational( src_gps_info.dest_longitude[0] ); + dst_exif->fGPSDestLongitude[1] = convert_to_dng_urational( src_gps_info.dest_longitude[1] ); + dst_exif->fGPSDestLongitude[2] = convert_to_dng_urational( src_gps_info.dest_longitude[2] ); + + dst_exif->fGPSDestBearingRef.Set( src_gps_info.dest_bearing_ref ); + + dst_exif->fGPSDestBearing = convert_to_dng_urational( src_gps_info.dest_bearing ); + + dst_exif->fGPSDestDistanceRef.Set( src_gps_info.dest_distance_ref ); + + dst_exif->fGPSDestDistance = convert_to_dng_urational( src_gps_info.dest_distance ); + + dst_exif->fGPSProcessingMethod.Set( src_gps_info.processing_method ); + + dst_exif->fGPSAreaInformation.Set( src_gps_info.area_information ); + + dst_exif->fGPSDateStamp.Set( src_gps_info.date_stamp ); + + dst_exif->fGPSDifferential = src_gps_info.differential; + } +} + +static void convert_dng_exif_to_dng_exif_info( gpr_exif_info* dst_exif, const dng_exif* src_exif ) +{ + assert(src_exif->fModel.Length() < sizeof(dst_exif->camera_model)); + strcpy( dst_exif->camera_model, src_exif->fModel.Get() ); + + assert(src_exif->fMake.Length() < sizeof(dst_exif->camera_make)); + strcpy( dst_exif->camera_make, src_exif->fMake.Get() ); + + assert(src_exif->fCameraSerialNumber.Length() < sizeof(dst_exif->camera_serial)); + strcpy( dst_exif->camera_serial, src_exif->fCameraSerialNumber.Get() ); + + assert(src_exif->fImageDescription.Length() < sizeof(dst_exif->image_description)); + strcpy( dst_exif->image_description, src_exif->fImageDescription.Get() ); + + dst_exif->aperture = convert_to_unsigned_rational( src_exif->fMaxApertureValue ); + dst_exif->f_stop_number = convert_to_unsigned_rational( src_exif->fFNumber ); + dst_exif->exposure_time = convert_to_unsigned_rational( src_exif->fExposureTime ); + dst_exif->focal_length = convert_to_unsigned_rational( src_exif->fFocalLength ); + dst_exif->digital_zoom = convert_to_unsigned_rational( src_exif->fDigitalZoomRatio ); + dst_exif->exposure_bias = convert_to_signed_rational( src_exif->fExposureBiasValue ); + + dst_exif->metering_mode = (gpr_metering_mode)src_exif->fMeteringMode; + dst_exif->focal_length_in_35mm_film = src_exif->fFocalLengthIn35mmFilm; + dst_exif->exposure_program = (gpr_exposure_program)src_exif->fExposureProgram; + dst_exif->light_source = (gpr_light_source)src_exif->fLightSource; + dst_exif->flash = (gpr_flash)src_exif->fFlash; + dst_exif->sensing_method = (gpr_sensing_method)src_exif->fSensingMethod; + dst_exif->file_source = (gpr_file_source)src_exif->fFileSource; + dst_exif->scene_type = (gpr_scene_type)src_exif->fSceneType; + dst_exif->white_balance = (gpr_white_balance)src_exif->fWhiteBalance; + dst_exif->exposure_mode = (gpr_exposure_mode)src_exif->fExposureMode; + dst_exif->scene_capture_type = (gpr_scene_capture_type)src_exif->fSceneCaptureType; + dst_exif->gain_control = (gpr_gain_control)src_exif->fGainControl; + dst_exif->contrast = (gpr_contrast)src_exif->fContrast; + dst_exif->saturation = src_exif->fSaturation; + dst_exif->sharpness = (gpr_sharpness)src_exif->fSharpness; + + dst_exif->iso_speed_rating = src_exif->fISOSpeedRatings[0]; + + dst_exif->date_time_original = convert_to_dng_date_and_time( src_exif->fDateTimeOriginal.DateTime() ); + dst_exif->date_time_digitized = convert_to_dng_date_and_time( src_exif->fDateTimeOriginal.DateTime() ); + + assert(src_exif->fSoftware.Length() < sizeof(dst_exif->software_version)); + memcpy( dst_exif->software_version, src_exif->fSoftware.Get(), src_exif->fSoftware.Length() ); + + assert(src_exif->fUserComment.Length() < sizeof(dst_exif->user_comment)); + memcpy( dst_exif->user_comment, src_exif->fUserComment.Get(), src_exif->fUserComment.Length() ); + + // GPS Info + gpr_gps_info& dst_gps_info = dst_exif->gps_info; + + dst_gps_info.version_id = src_exif->fGPSVersionID; + + dst_gps_info.gps_info_valid = dst_gps_info.version_id > 0; + + strcpy( dst_gps_info.latitude_ref, src_exif->fGPSLatitudeRef.Get() ); + + dst_gps_info.latitude[0] = convert_to_unsigned_rational( src_exif->fGPSLatitude[0] ); + dst_gps_info.latitude[1] = convert_to_unsigned_rational( src_exif->fGPSLatitude[1] ); + dst_gps_info.latitude[2] = convert_to_unsigned_rational( src_exif->fGPSLatitude[2] ); + + strcpy( dst_gps_info.longitude_ref, src_exif->fGPSLongitudeRef.Get() ); + + dst_gps_info.longitude[0] = convert_to_unsigned_rational( src_exif->fGPSLongitude[0] ); + dst_gps_info.longitude[1] = convert_to_unsigned_rational( src_exif->fGPSLongitude[1] ); + dst_gps_info.longitude[2] = convert_to_unsigned_rational( src_exif->fGPSLongitude[2] ); + + dst_gps_info.altitude_ref = src_exif->fGPSAltitudeRef; + + dst_gps_info.altitude = convert_to_unsigned_rational( src_exif->fGPSAltitude ); + + dst_gps_info.time_stamp[0] = convert_to_unsigned_rational( src_exif->fGPSTimeStamp[0] ); + dst_gps_info.time_stamp[1] = convert_to_unsigned_rational( src_exif->fGPSTimeStamp[1] ); + dst_gps_info.time_stamp[2] = convert_to_unsigned_rational( src_exif->fGPSTimeStamp[2] ); + + strcpy( dst_gps_info.satellites, src_exif->fGPSSatellites.Get() ); + + strcpy( dst_gps_info.status, src_exif->fGPSStatus.Get() ); + + strcpy( dst_gps_info.measure_mode, src_exif->fGPSMeasureMode.Get() ); + + dst_gps_info.dop = convert_to_unsigned_rational( src_exif->fGPSDOP ); + + strcpy( dst_gps_info.speed_ref, src_exif->fGPSSpeedRef.Get() ); + + dst_gps_info.speed = convert_to_unsigned_rational( src_exif->fGPSSpeed ); + + strcpy( dst_gps_info.track_ref, src_exif->fGPSTrackRef.Get() ); + + dst_gps_info.track = convert_to_unsigned_rational( src_exif->fGPSTrack ); + + strcpy( dst_gps_info.img_direction_ref, src_exif->fGPSImgDirectionRef.Get() ); + + dst_gps_info.img_direction = convert_to_unsigned_rational( src_exif->fGPSImgDirection ); + + strcpy( dst_gps_info.map_datum, src_exif->fGPSMapDatum.Get() ); + + strcpy( dst_gps_info.dest_latitude_ref, src_exif->fGPSDestLatitudeRef.Get() ); + + dst_gps_info.dest_latitude[0] = convert_to_unsigned_rational( src_exif->fGPSDestLatitude[0] ); + dst_gps_info.dest_latitude[1] = convert_to_unsigned_rational( src_exif->fGPSDestLatitude[1] ); + dst_gps_info.dest_latitude[2] = convert_to_unsigned_rational( src_exif->fGPSDestLatitude[2] ); + + strcpy( dst_gps_info.dest_longitude_ref, src_exif->fGPSDestLongitudeRef.Get() ); + + dst_gps_info.dest_longitude[0] = convert_to_unsigned_rational( src_exif->fGPSDestLongitude[0] ); + dst_gps_info.dest_longitude[1] = convert_to_unsigned_rational( src_exif->fGPSDestLongitude[1] ); + dst_gps_info.dest_longitude[2] = convert_to_unsigned_rational( src_exif->fGPSDestLongitude[2] ); + + strcpy( dst_gps_info.dest_bearing_ref, src_exif->fGPSDestBearingRef.Get() ); + + dst_gps_info.dest_bearing = convert_to_unsigned_rational( src_exif->fGPSDestBearing ); + + strcpy( dst_gps_info.dest_distance_ref, src_exif->fGPSDestDistanceRef.Get() ); + + dst_gps_info.dest_distance = convert_to_unsigned_rational( src_exif->fGPSDestDistance ); + + strcpy( dst_gps_info.processing_method, src_exif->fGPSProcessingMethod.Get() ); + + strcpy( dst_gps_info.area_information, src_exif->fGPSAreaInformation.Get() ); + + strcpy( dst_gps_info.date_stamp, src_exif->fGPSDateStamp.Get() ); + + dst_gps_info.differential = src_exif->fGPSDifferential; +} + + +#define MAX_BUF_SIZE 16000 + +static char _warp_rect_buffer [256]; + +static bool read_dng(const gpr_allocator* allocator, + dng_stream* dng_read_stream, + gpr_buffer_auto* raw_image_buffer, + gpr_buffer_auto* vc5_image_buffer, + gpr_parameters* convert_params = NULL, + bool* is_vc5_format = NULL ) +{ + dng_host host; + + gpr_buffer_auto vc5_image_obj( allocator->Alloc, allocator->Free ); + + static uint32 gPreferredSize = 0; + static uint32 gMinimumSize = 0; + static uint32 gMaximumSize = 0; + + static dng_string gDumpDNG; + + host.SetPreferredSize (gPreferredSize); + host.SetMinimumSize (gMinimumSize ); + host.SetMaximumSize (gMaximumSize ); + + host.ValidateSizes (); + + host.SetSaveDNGVersion (dngVersion_SaveDefault); + + host.SetSaveLinearDNG (false); + + host.SetKeepOriginalFile (false); + + AutoPtr negative; + + if( raw_image_buffer != NULL && vc5_image_buffer == NULL ) + { + vc5_image_buffer = &vc5_image_obj; + } + + { + dng_info info; + + info.Parse (host, *dng_read_stream); + + info.PostParse (host); + + if (!info.IsValidDNG ()) + { + return false; + } + + dng_memory_block* gpmf_payload = host.GetGPMFPayload().Get(); + + if( gpmf_payload && gpmf_payload->LogicalSize() > 0 ) + { + if( convert_params != NULL && convert_params->gpmf_payload.buffer == NULL && convert_params->gpmf_payload.size == 0 ) + { + convert_params->gpmf_payload.size = gpmf_payload->LogicalSize(); + convert_params->gpmf_payload.buffer = allocator->Alloc( convert_params->gpmf_payload.size ); + + memcpy( convert_params->gpmf_payload.buffer, gpmf_payload->Buffer(), convert_params->gpmf_payload.size ); + } + } + + negative.Reset (host.Make_dng_negative ()); + + negative->Parse (host, *dng_read_stream, info); + + negative->PostParse (host, *dng_read_stream, info); + +#if GPR_READING + if( negative->IsVc5Image( info ) ) + { + gpr_read_image reader( vc5_image_buffer ); + + if( vc5_image_buffer == NULL ) + { + reader.SetReadVC5(false); + } + + if( raw_image_buffer == NULL ) + { + reader.SetDecodeVC5(false); + } + + negative->ReadVc5Image(host, *dng_read_stream, info, reader); + + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex].Get (); + + if (rawIFD.fOpcodeList2Count) + { + negative->OpcodeList2().Parse (host, *dng_read_stream, rawIFD.fOpcodeList2Count, rawIFD.fOpcodeList2Offset); + } + + if (rawIFD.fOpcodeList3Count) + { + negative->OpcodeList3().Parse (host, *dng_read_stream, rawIFD.fOpcodeList3Count, rawIFD.fOpcodeList3Offset); + } + + if( is_vc5_format ) + *is_vc5_format = true; + } + else +#endif + { + negative->ReadStage1Image (host, *dng_read_stream, info); + + if( is_vc5_format ) + *is_vc5_format = false; + } + + const dng_image& raw_image = negative->RawImage(); + + if( convert_params ) + { + dng_rect bounds = raw_image.Bounds(); + int i,j; + + convert_params->input_width = bounds.W(); + convert_params->input_height = bounds.H(); + convert_params->input_pitch = convert_params->input_width * 2; + + // Copy ColorMatrix1, ColorMatrix2 + { + const dng_camera_profile &profile_info = negative->ProfileByIndex( 0 ); + const dng_matrix &m1 = profile_info.ColorMatrix1(); + const dng_matrix &m2 = profile_info.ColorMatrix2(); + + for (i = 0; i < 3; i++) + { + for (j = 0; j < 3; j++) + { + convert_params->profile_info.color_matrix_1[i][j] = m1[i][j]; + convert_params->profile_info.color_matrix_2[i][j] = m2[i][j]; + } + } + + convert_params->profile_info.compute_color_matrix = false; + convert_params->profile_info.matrix_weighting = 1.0; + + memset( convert_params->profile_info.wb1, 0, sizeof(convert_params->profile_info.wb1) ); + memset( convert_params->profile_info.wb2, 0, sizeof(convert_params->profile_info.wb2) ); + + memset( convert_params->profile_info.cam_to_srgb_1, 0, sizeof(convert_params->profile_info.cam_to_srgb_1) ); + memset( convert_params->profile_info.cam_to_srgb_2, 0, sizeof(convert_params->profile_info.cam_to_srgb_2) ); + } + + // Set Exif Info + convert_dng_exif_to_dng_exif_info( &convert_params->exif_info, negative->GetExif() ); + + // Set Tuning Info + { + gpr_tuning_info& tuning_info = convert_params->tuning_info; + + tuning_info.orientation = (GPR_ORIENTATION)negative->BaseOrientation().GetAdobe(); + + if( negative->HasCameraNeutral() ) + { + const dng_vector& camNeutral = negative->CameraNeutral(); + + tuning_info.wb_gains.r_gain = 1 / camNeutral[0]; + tuning_info.wb_gains.g_gain = 1 / camNeutral[1]; + tuning_info.wb_gains.b_gain = 1 / camNeutral[2]; + } + + const dng_linearization_info& linearization_info = *negative->GetLinearizationInfo(); + + { + gpr_static_black_level& static_black_level = tuning_info.static_black_level; + + static_black_level.r_black = linearization_info.fBlackLevel[0][0][0]; + static_black_level.g_r_black = linearization_info.fBlackLevel[0][1][0]; + static_black_level.g_b_black = linearization_info.fBlackLevel[1][0][0]; + static_black_level.b_black = linearization_info.fBlackLevel[1][1][0]; + } + + { + gpr_saturation_level& dgain_saturation_level = tuning_info.dgain_saturation_level; + + dgain_saturation_level.level_red = linearization_info.fWhiteLevel[0]; + dgain_saturation_level.level_green_even = dgain_saturation_level.level_red; + dgain_saturation_level.level_green_odd = dgain_saturation_level.level_red; + dgain_saturation_level.level_blue = dgain_saturation_level.level_red; + } + + { + dng_ifd &rawIFD = *info.fIFD [info.fMainIndex].Get (); + + gpr_saturation_level& dgain_saturation_level = tuning_info.dgain_saturation_level; + + bool rggb_raw = (rawIFD.fCFAPattern[0][0] == 0) && (rawIFD.fCFAPattern[0][1] == 1) && (rawIFD.fCFAPattern[1][0] == 1) && (rawIFD.fCFAPattern[1][1] == 2); + + if( rggb_raw ) + { + if( dgain_saturation_level.level_red == 4095 && + dgain_saturation_level.level_green_even == 4095 && + dgain_saturation_level.level_green_odd == 4095 && + dgain_saturation_level.level_blue == 4095 ) + { + tuning_info.pixel_format = PIXEL_FORMAT_RGGB_12; + } + else if(dgain_saturation_level.level_red == 16383 && + dgain_saturation_level.level_green_even == 16383 && + dgain_saturation_level.level_green_odd == 16383 && + dgain_saturation_level.level_blue == 16383 ) + { + tuning_info.pixel_format = PIXEL_FORMAT_RGGB_14; + } + else + { + assert(0); + return false; + } + } + else + { + if( dgain_saturation_level.level_red == 4095 && + dgain_saturation_level.level_green_even == 4095 && + dgain_saturation_level.level_green_odd == 4095 && + dgain_saturation_level.level_blue == 4095 ) + { + tuning_info.pixel_format = PIXEL_FORMAT_GBRG_12; + } + else + { + assert(0); + return false; + } + + + } + } + + // Noise profile + if ( negative->HasNoiseProfile() ) + { + dng_noise_profile noise_profile = negative->NoiseProfile(); + + dng_noise_function noise_function = noise_profile.NoiseFunction (0); + + tuning_info.noise_scale = noise_function.Scale(); + tuning_info.noise_offset = noise_function.Offset(); + //LogPrint( "Noise profile s = %f, o = %f ", tuning_info.noise_scale, tuning_info.noise_offset ); + } + + // GainMap + dng_opcode_list &opcodelist2 = negative->OpcodeList2 (); + uint32_t count = opcodelist2.Count (); + // Note: this code will have to get smarter if we ever have anything other than four GainMap tags in OpcodeList2 + if ( count == 4 && tuning_info.gain_map.size == 0 ) + { + char gainmap_buffer [4][MAX_BUF_SIZE]; + + for ( int i = 0; i < 4; i++ ) + { + // Get GainMap Opcode + dng_opcode &opcode = opcodelist2.Entry( i ); + + // generate stream data + dng_stream stream ( gainmap_buffer[i], MAX_BUF_SIZE ); + stream.Put_uint32 ( 0x01040000 ); // version + stream.Put_uint32 ( 0x3 ); // flags + opcode.PutData( stream ); + + // Point to buffer + if( i == 0 ) + tuning_info.gain_map.size = stream.Position(); + + assert( tuning_info.gain_map.buffers[i] == NULL ); + + tuning_info.gain_map.buffers[i] = (char*)allocator->Alloc( tuning_info.gain_map.size ); + memcpy( tuning_info.gain_map.buffers[i], gainmap_buffer[i], tuning_info.gain_map.size ); + } + } + else + { + tuning_info.gain_map.size = 0; + } + + // WarpRectilinear + dng_opcode_list &opcodelist3 = negative->OpcodeList3 (); + count = opcodelist3.Count (); + // Note: this code will have to get smarter if we ever have anything other than one WarpRectilinear tag in OpcodeList3 + if ( count == 1 ) + { + // Get WarpRectilinear Opcode + dng_opcode &opcode = opcodelist3.Entry( 0 ); + + dng_stream stream ( _warp_rect_buffer, 256 ); + opcode.PutData( stream ); + + // Ugly way to get the parameters, but I couldn't figure how else to get access to the data + double red_coefficient = * (double *) &_warp_rect_buffer[8]; + double blue_coefficient = * (double *) &_warp_rect_buffer[8 + 2*6*8]; + //LogPrint( "WarpRectilinear red = %f, blue = %f ", red_coefficient, blue_coefficient ); + + tuning_info.warp_red_coefficient = red_coefficient; + tuning_info.warp_blue_coefficient = blue_coefficient; + } + else + { + tuning_info.warp_red_coefficient = 0; + tuning_info.warp_blue_coefficient = 0; + } + } + } + + if( raw_image_buffer ) + { + CopyRawImageToBuffer( raw_image, *raw_image_buffer ); + } + } + + return true; +} + + +void reduction(double a[][6], int size, int pivot, int col) +{ + int i, j; + double factor; + factor = a[pivot][col]; + + for (i = 0; i < 2 * size; i++) { + a[pivot][i] /= factor; + } + + for (i = 0; i < size; i++) { + if (i != pivot) { + factor = a[i][col]; + for (j = 0; j < 2 * size; j++) { + a[i][j] = a[i][j] - a[pivot][j] * factor; + } + } + } +} + +void calc_color_matrix( double in_matrix[3][3], double wb[3], double weight, double out_matrix[3][3] ) +{ + double temp1[3][3]; + double temp2[3][3]; + + int i,j,k; + +#if PRINT_MATRIX + LogPrint("\nOriginal Matrix"); + for (i = 0; i < 3; i++) + LogPrint("%8.5f %8.5f %8.5f", in_matrix[i][0], in_matrix[i][1], in_matrix[i][2] ); +#endif + + // Interpolate with identity matrix by weight w + double w = weight; + double z = 1.0 - weight; + + for (i = 0; i < 3; i++ ) + { + for (j = 0; j < 3; j++ ) + temp1[i][j] = in_matrix[i][j] * w; + + temp1[i][i] += z; + } + +#if PRINT_MATRIX + LogPrint("\nInterpolated Matrix"); + for (i = 0; i < 3; i++) + LogPrint("%8.5f %8.5f %8.5f", temp1[i][0], temp1[i][1], temp1[i][2] ); +#endif + + // Multiply matrix by sRGB_to_XYZd50 (from http://www.brucelindbloom.com) + double sRGB_to_XYZd50[3][3] = {{0.4361, 0.3851, 0.1431}, {0.2225, 0.7169, 0.0606}, {0.0139, 0.0971, 0.7142}}; + + double sum; + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + { + sum = 0; + for (k = 0; k < 3; k++) + sum = sum + sRGB_to_XYZd50[i][k] * temp1[k][j]; + + temp2[i][j] = sum; + } + +#if PRINT_MATRIX + LogPrint("\ntimes sRGB_to_XYZd50"); + for (i = 0; i < 3; i++) + LogPrint("%8.5f %8.5f %8.5f", temp2[i][0], temp2[i][1], temp2[i][2] ); +#endif + + // Set up diagonal matrix with white balance gains + double wb_diag[3][3]; + for (i = 0; i < 3; i++) + { + for (j = 0; j < 3; j++) + wb_diag[i][j] = 0.0; + + wb_diag[i][i] = wb[i]; + } + + // Multiply by white balance gains + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + { + sum = 0; + for (k = 0; k < 3; k++) + sum = sum + temp2[i][k] * wb_diag[k][j]; + + temp1[i][j] = sum; + } + +#if PRINT_MATRIX + LogPrint("\ntimes wb"); + for (i = 0; i < 3; i++) + LogPrint("%8.5f %8.5f %8.5f", temp1[i][0], temp1[i][1], temp1[i][2] ); +#endif + + // Invert the resulting matrix + double matrix[3][6]; + + for (i = 0; i < 3; i++) + for (j = 0; j < 6; j++) + if (j == i + 3) + matrix[i][j] = 1; + else + matrix[i][j] = 0; + + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + matrix[i][j] = temp1[i][j]; + + for (i = 0; i < 3; i++) + reduction(matrix, 3, i, i); + + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + out_matrix[i][j] = matrix[i][j+3]; + +#if PRINT_MATRIX + LogPrint("\nInverse Matrix"); + for (i = 0; i < 3; i++) { + LogPrint("%8.5f %8.5f %8.5f", out_matrix[i][0], out_matrix[i][1], out_matrix[i][2] ); + } +#endif + +} + +typedef struct +{ + unsigned char* orig_dst; /* Address to the memory location that this buffer points to */ + + unsigned char* next_dst; + +} jpg_write_context; + +void write_jpg_thumbnail(void* context, void* data, int size) +{ + jpg_write_context* _context = (jpg_write_context*)context; + + memcpy( _context->next_dst, data, size ); + + _context->next_dst = (unsigned char*)_context->next_dst + size; +} + +static void write_dng(const gpr_allocator* allocator, + dng_stream* dng_write_stream, + const gpr_buffer_auto* raw_image_buffer, + bool compress_raw_to_vc5, + gpr_buffer_auto* vc5_image_buffer, + const gpr_parameters* convert_params ) +{ + gpr_profile_info* profile_info = (gpr_profile_info *) &convert_params->profile_info; + const gpr_exif_info* exif_info = &convert_params->exif_info; + + const bool vc5_dng = compress_raw_to_vc5 || vc5_image_buffer; + + int i,j; + + int activeWidth = convert_params->input_width; + int activeHeight = convert_params->input_height; + + int outputWidth = activeWidth; + int outputHeight = activeHeight; + + gDNGShowTimers = false; + + dng_memory_allocator memalloc(gDefaultDNGMemoryAllocator); + + dng_rect rect(outputHeight, outputWidth); + dng_host host(&memalloc); + + host.SetSaveDNGVersion(dngVersion_SaveDefault); + host.SetSaveLinearDNG(false); + host.SetKeepOriginalFile(true); + + AutoPtr image(new dng_simple_image(rect, 1, ttShort, memalloc)); + + gpr_buffer_auto raw_allocated_buffer( allocator->Alloc, allocator->Free ); + + if( raw_image_buffer == NULL && vc5_image_buffer ) + { +#if GPR_READING + vc5_decoder_parameters vc5_decoder_params; + + vc5_decoder_parameters_set_default(&vc5_decoder_params); + + vc5_decoder_params.mem_alloc = allocator->Alloc; + vc5_decoder_params.mem_free = allocator->Free; + + switch(convert_params->tuning_info.pixel_format) + { + case PIXEL_FORMAT_RGGB_12: + vc5_decoder_params.pixel_format = VC5_DECODER_PIXEL_FORMAT_RGGB_12; + break; + + case PIXEL_FORMAT_RGGB_14: + vc5_decoder_params.pixel_format = VC5_DECODER_PIXEL_FORMAT_RGGB_14; + break; + + case PIXEL_FORMAT_GBRG_12: + vc5_decoder_params.pixel_format = VC5_DECODER_PIXEL_FORMAT_GBRG_12; + break; + + default: + assert(0); + return; + }; + + gpr_buffer vc5_image = { vc5_image_buffer->get_buffer(), vc5_image_buffer->get_size() }; + gpr_buffer raw_image = { raw_allocated_buffer.get_buffer(), raw_allocated_buffer.get_size() }; + + if( vc5_decoder_process( &vc5_decoder_params, &vc5_image, &raw_image, NULL ) != CODEC_ERROR_OKAY ) + { + assert(0); + } + + raw_allocated_buffer.set( raw_image.buffer, raw_image.size, true ); + + raw_image_buffer = &raw_allocated_buffer; +#else + return; // Since vc5 decoder is not enabled, we cannot decode this vc5 bitstream to create raw image +#endif // GPR_READING + } + + unsigned int input_pitch = convert_params->input_pitch; + + if( ( convert_params->tuning_info.pixel_format == PIXEL_FORMAT_GBRG_12P || + convert_params->tuning_info.pixel_format == PIXEL_FORMAT_RGGB_12P ) && + vc5_dng == false ) + { + unpack_pixel_format( raw_image_buffer, convert_params, &raw_allocated_buffer ); + + raw_image_buffer = &raw_allocated_buffer; + + input_pitch = convert_params->input_width * 2; + } + + if( vc5_dng == false ) + { + CopyBufferToRawImage( *raw_image_buffer, input_pitch / sizeof(short), *(image.Get()) ); + } + + AutoPtr negative(host.Make_dng_negative()); + + negative->SetOriginalBestQualityFinalSize( dng_point(outputHeight, outputWidth) ); + + negative->SetOriginalDefaultFinalSize( dng_point(outputHeight, outputWidth) ); + + { // Set Tuning Info + const gpr_tuning_info* tuning_info = &convert_params->tuning_info; + + const gpr_static_black_level static_black_level = tuning_info->static_black_level; + + switch( convert_params->tuning_info.pixel_format ) + { + case PIXEL_FORMAT_RGGB_12: + case PIXEL_FORMAT_RGGB_12P: + case PIXEL_FORMAT_RGGB_14: + negative->SetQuadBlacks(static_black_level.r_black, + static_black_level.g_r_black, + static_black_level.g_b_black, + static_black_level.b_black, + -1 ); + break; + case PIXEL_FORMAT_GBRG_12: + case PIXEL_FORMAT_GBRG_12P: + negative->SetQuadBlacks(static_black_level.g_b_black, + static_black_level.b_black, + static_black_level.r_black, + static_black_level.g_r_black, + -1 ); + break; + + default: + assert(0); + } + + const gpr_saturation_level dgain_saturation_level = tuning_info->dgain_saturation_level; + + if( dgain_saturation_level.level_red == dgain_saturation_level.level_green_even && + dgain_saturation_level.level_red == dgain_saturation_level.level_green_odd && + dgain_saturation_level.level_red == dgain_saturation_level.level_blue ) + { + negative->SetWhiteLevel( dgain_saturation_level.level_red, -1 ); + } + else + { + negative->SetWhiteLevel( dgain_saturation_level.level_red, 0 ); + negative->SetWhiteLevel( dgain_saturation_level.level_green_even, 1 ); + negative->SetWhiteLevel( dgain_saturation_level.level_green_odd, 2 ); + negative->SetWhiteLevel( dgain_saturation_level.level_blue, 3 ); + } + + negative->SetBaseOrientation((dng_orientation::AdobeToDNG(tuning_info->orientation))); + + dng_vector camNeutral(3); + + camNeutral[0] = 1.0 / tuning_info->wb_gains.r_gain; + camNeutral[1] = 1.0 / tuning_info->wb_gains.g_gain; + camNeutral[2] = 1.0 / tuning_info->wb_gains.b_gain; + + negative->SetCameraNeutral(camNeutral); + + + // Add noise profile + if ( tuning_info->noise_scale > 0 ) + { + std::vector noiseFunctions; + noiseFunctions.push_back( dng_noise_function ( tuning_info->noise_scale, tuning_info->noise_offset ) ); + negative->SetNoiseProfile( dng_noise_profile ( noiseFunctions ) ); + } + + size_t gain_map_size = tuning_info->gain_map.size; + + // GainMap - aka vignette or shading correction (one for each CFA channel)(applied before demosaicking (OpcodeList2)) + if ( gain_map_size > 0 && tuning_info->gain_map.buffers[0] != 0 && tuning_info->gain_map.buffers[1] != 0 && tuning_info->gain_map.buffers[2] != 0 && tuning_info->gain_map.buffers[3] != 0 ) + { + dng_opcode_list &opcodelist2 = negative->OpcodeList2 (); + + dng_stream gain_map_stream0 (tuning_info->gain_map.buffers[0], gain_map_size); + AutoPtr gain_map_opcode0 ( new dng_opcode_GainMap ( host, gain_map_stream0 )); + opcodelist2.Append( gain_map_opcode0 ); + + dng_stream gain_map_stream1 (tuning_info->gain_map.buffers[1], gain_map_size); + AutoPtr gain_map_opcode1 ( new dng_opcode_GainMap ( host, gain_map_stream1 )); + opcodelist2.Append( gain_map_opcode1 ); + + dng_stream gain_map_stream2 (tuning_info->gain_map.buffers[2], gain_map_size); + AutoPtr gain_map_opcode2 ( new dng_opcode_GainMap ( host, gain_map_stream2 )); + opcodelist2.Append( gain_map_opcode2 ); + + dng_stream gain_map_stream3 (tuning_info->gain_map.buffers[3], gain_map_size); + AutoPtr gain_map_opcode3 ( new dng_opcode_GainMap ( host, gain_map_stream3 )); + opcodelist2.Append( gain_map_opcode3 ); + } + + // WarpRectilinear - aka chromatic aberration correction (applied after demosaicking (OpcodeList3)) + if ( tuning_info->warp_red_coefficient > 0 && tuning_info->warp_blue_coefficient > 0 ) + { + dng_opcode_list &opcodelist3 = negative->OpcodeList3 (); + + dng_warp_params_rectilinear chromatic_aberration; + + chromatic_aberration.fPlanes = 3; + chromatic_aberration.fCenter = dng_point_real64( 0.5, 0.5 ); + chromatic_aberration.fRadParams[0][0] = tuning_info->warp_red_coefficient; + chromatic_aberration.fRadParams[1][0] = 1.0; + chromatic_aberration.fRadParams[2][0] = tuning_info->warp_blue_coefficient; + + AutoPtr warp_opcode ( new dng_opcode_WarpRectilinear ( chromatic_aberration, 0x03 )); + + opcodelist3.Append( warp_opcode ); + } + } + + //GP!! NEED outputWidth, activeWidth, outputHeight, activeHeight here + negative->SetDefaultScale(dng_urational(outputWidth, activeWidth), dng_urational(outputHeight, activeHeight)); + + uint32 crop_size_val = 0; + dng_point crop_origin( 0, 0 ); + dng_point crop_size( activeHeight - 2 * crop_size_val, activeWidth - 2 * crop_size_val ); + + negative->SetDefaultCropOrigin( crop_origin.h, crop_origin.v ); + negative->SetDefaultCropSize( crop_size.h, crop_size.v ); + + negative->SetOriginalDefaultCropSize( dng_urational(crop_size.h, 1), dng_urational(crop_size.v, 1) ); + + { + dng_rect activeArea = dng_rect(activeHeight, activeWidth); + + negative->SetActiveArea(activeArea); + } + + char camera_make_and_model[256]; + + gpr_exif_info_get_camera_make_and_model(exif_info, camera_make_and_model); + + negative->SetModelName( camera_make_and_model ); + + negative->SetLocalName( camera_make_and_model ); + + //GP!! "Raw file?" + negative->SetOriginalRawFileName("RAW FILE"); + //GP!! Either 1 or 3 + negative->SetColorChannels(3); + + //!! Need correct CFA sequence here!! + ColorKeyCode colorCodes[4] = { colorKeyRed, colorKeyGreen, colorKeyBlue, colorKeyGreen }; + // ColorKeyCode colorCodes[4] = { colorKeyGreen, colorKeyRed, colorKeyGreen, colorKeyBlue }; + + negative->SetColorKeys(colorCodes[0], colorCodes[1], colorCodes[2], colorCodes[3]); + + // Set Bayer Pattern + if( convert_params->tuning_info.pixel_format == PIXEL_FORMAT_RGGB_12 || convert_params->tuning_info.pixel_format == PIXEL_FORMAT_RGGB_12P || convert_params->tuning_info.pixel_format == PIXEL_FORMAT_RGGB_14 ) + { + negative->SetBayerMosaic(1); + } + else if( convert_params->tuning_info.pixel_format == PIXEL_FORMAT_GBRG_12 || convert_params->tuning_info.pixel_format == PIXEL_FORMAT_GBRG_12P ) + { + negative->SetBayerMosaic(3); + } + else + { + assert(0); + return; + } + + negative->SetBaselineExposure(0); + negative->SetBaselineNoise(1.0); + negative->SetBaselineSharpness(1.0); + + negative->SetAntiAliasStrength(dng_urational(100, 100)); + negative->SetLinearResponseLimit(1.0); + negative->SetShadowScale( dng_urational(1, 1) ); + negative->SetAnalogBalance(dng_vector_3(1.0, 1.0, 1.0)); + + AutoPtr prof(new dng_camera_profile); + prof->SetName( camera_make_and_model ); + + dng_matrix_3by3 mColor1; + dng_matrix_3by3 mColor2; + + if ( profile_info->compute_color_matrix ) + { + double matrix_weighting = profile_info->matrix_weighting; + + double out_matrix[3][3]; + + if ( matrix_weighting < 0.0 || matrix_weighting > 1.0 ) + matrix_weighting = 1.0; + + calc_color_matrix( profile_info->cam_to_srgb_1, profile_info->wb1, matrix_weighting, out_matrix ); + + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + mColor1[i][j] = out_matrix[i][j]; + + calc_color_matrix( profile_info->cam_to_srgb_2, profile_info->wb2, matrix_weighting, out_matrix ); + + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + mColor2[i][j] = out_matrix[i][j]; + } + else + { + for (i = 0; i < 3; i++) + for (j = 0; j < 3; j++) + { + mColor1[i][j] = profile_info->color_matrix_1[i][j]; + mColor2[i][j] = profile_info->color_matrix_2[i][j]; + } + } + +#if PRINT_MATRIX + LogPrint("CM1:"); + for (i = 0; i < 3; i++) + LogPrint(" %8.5f %8.5f %8.5f", mColor1[i][0], mColor1[i][1], mColor1[i][2] ); + LogPrint("CM2:"); + for (i = 0; i < 3; i++) + LogPrint(" %8.5f %8.5f %8.5f", mColor2[i][0], mColor2[i][1], mColor2[i][2] ); +#endif + + prof->SetColorMatrix1((dng_matrix) mColor1); + prof->SetColorMatrix2((dng_matrix) mColor2); + + prof->SetCalibrationIlluminant1(profile_info->illuminant1); + prof->SetCalibrationIlluminant2(profile_info->illuminant2); + + negative->AddProfile(prof); + + dng_exif* const exif = negative->GetExif(); + exif->fModel.Set( exif_info->camera_model ); + exif->fMake.Set( exif_info->camera_make ); + + convert_dng_exif_info_to_dng_exif(exif, exif_info ); + + { + dng_date_time_info date_time_original; + date_time_original.SetDateTime( convert_to_dng_date_time( exif_info->date_time_original ) ); + + negative->UpdateDateTime(date_time_original); + } + + if( convert_params->gpmf_payload.buffer != NULL && convert_params->gpmf_payload.size > 0 ) + { // Set GPMF Data + dng_string gopro_tag; + gopro_tag.Append("GoPro\n"); + + AutoPtr gpmf_buffer; + + gpmf_buffer.Reset (host.Allocate(gopro_tag.Length() + convert_params->gpmf_payload.size) ); + + unsigned char* gpmf_buffer_ptr = (unsigned char*)gpmf_buffer.Get()->Buffer(); + + memcpy( gpmf_buffer_ptr + 0, gopro_tag.Get(), gopro_tag.Length() ); + memcpy( gpmf_buffer_ptr + gopro_tag.Length(), convert_params->gpmf_payload.buffer, convert_params->gpmf_payload.size ); + + negative->SetPrivateData(gpmf_buffer); + } + + negative->SetStage1Image(image); + + dng_preview_list* preview_list = NULL; + + dng_image_writer* writer = NULL; + +#if GPR_WRITING + if( vc5_dng ) + { + gpr_image_writer* gpr_writer = new gpr_image_writer(raw_image_buffer, convert_params->input_width, convert_params->input_height, convert_params->input_pitch, vc5_image_buffer ); + set_vc5_encoder_parameters( gpr_writer->GetVc5EncoderParams(), convert_params ); + + gpr_writer->EncodeVc5Image(); + + if( convert_params->enable_preview ) + { + const gpr_preview_image& preview_image = convert_params->preview_image; + + if( preview_image.jpg_preview.size > 0 && preview_image.jpg_preview.buffer != NULL ) + { + preview_list = new dng_preview_list; + + AutoPtr jpeg_preview; + jpeg_preview.Reset(new dng_jpeg_preview); + jpeg_preview->fPhotometricInterpretation = piYCbCr; + + jpeg_preview->fInfo.fIsPrimary = true; + + jpeg_preview->fPreviewSize.v = preview_image.preview_height; + jpeg_preview->fPreviewSize.h = preview_image.preview_width; + jpeg_preview->fCompressedData.Reset(host.Allocate( preview_image.jpg_preview.size )); + memcpy( jpeg_preview->fCompressedData->Buffer_char(), preview_image.jpg_preview.buffer, preview_image.jpg_preview.size ); + + AutoPtr pp( dynamic_cast(jpeg_preview.Release()) ); + + preview_list->Append(pp); + } +#if GPR_JPEG_AVAILABLE + else + { + preview_list = new dng_preview_list; + + AutoPtr jpeg_preview; + jpeg_preview.Reset(new dng_jpeg_preview); + jpeg_preview->fPhotometricInterpretation = piYCbCr; + + jpeg_preview->fInfo.fIsPrimary = true; + + const gpr_rgb_buffer& rgb_buffer = gpr_writer->get_rgb_thumbnail(); + + gpr_buffer_auto buffer( allocator->Alloc, allocator->Free ); + + buffer.allocate(1024*1024); + + jpg_write_context context; + context.orig_dst = buffer.to_uchar(); + context.next_dst = context.orig_dst; + + tje_encode_with_func(write_jpg_thumbnail, (void*)&context, 2, rgb_buffer.width, rgb_buffer.height, 3, (const unsigned char*)rgb_buffer.buffer ); + + size_t size = context.next_dst - context.orig_dst; + jpeg_preview->fPreviewSize.v = rgb_buffer.height; + jpeg_preview->fPreviewSize.h = rgb_buffer.width; + jpeg_preview->fCompressedData.Reset(host.Allocate( size )); + memcpy( jpeg_preview->fCompressedData->Buffer_char(), buffer.get_buffer(), size ); + + AutoPtr pp( dynamic_cast(jpeg_preview.Release()) ); + + preview_list->Append(pp); + } +#endif + } + + writer = gpr_writer; + } + else +#endif + { + writer = new dng_image_writer; + } + + writer->SetComputeMd5Sum( convert_params->compute_md5sum ); + + assert(writer); + writer->WriteDNG(host, *dng_write_stream, *negative.Get(), preview_list, dngVersion_Current, vc5_dng == false ); + + delete writer; + + if( preview_list ) + delete preview_list; +} + +extern dng_memory_allocator gDefaultDNGMemoryAllocator; + +bool write_dngstream_to_buffer( dng_stream* stream, gpr_buffer* output_buffer, gpr_malloc mem_alloc, gpr_free mem_free ) +{ + size_t buffer_size = stream->Length(); + void* buffer = mem_alloc(buffer_size); + + stream->SetReadPosition(0); + stream->Get(buffer, buffer_size); + + output_buffer->buffer = buffer; + output_buffer->size = buffer_size; + + return true; +} + +bool gpr_parse_metadata(const gpr_allocator* allocator, + gpr_buffer* inp_dng_buffer, + gpr_parameters* parameters) +{ + dng_memory_stream inp_dng_stream( gDefaultDNGMemoryAllocator ); + inp_dng_stream.Put( inp_dng_buffer->buffer, inp_dng_buffer->size ); + inp_dng_stream.SetReadPosition(0); + + if( read_dng( allocator, &inp_dng_stream, NULL, NULL, parameters ) == false ) + { + assert(0); return false; + } + + return true; +} + +bool gpr_convert_raw_to_dng(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_raw_buffer, + gpr_buffer* out_dng_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_buffer_auto raw_buffer(allocator->Alloc, allocator->Free); + raw_buffer.set( (char*)inp_raw_buffer->buffer, inp_raw_buffer->size ); + + dng_memory_stream out_dng_stream( gDefaultDNGMemoryAllocator ); + + write_dng( allocator, &out_dng_stream, &raw_buffer, false, NULL, parameters ); + + write_dngstream_to_buffer( &out_dng_stream, out_dng_buffer, allocator->Alloc, allocator->Free ); + + TIMESTAMP("[END]", 1) + + return true; +} + +bool gpr_convert_dng_to_raw(const gpr_allocator* allocator, + gpr_buffer* inp_dng_buffer, + gpr_buffer* out_raw_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_buffer_auto raw_buffer(allocator->Alloc, allocator->Free); + + dng_memory_stream inp_dng_stream( gDefaultDNGMemoryAllocator ); + inp_dng_stream.Put( inp_dng_buffer->buffer, inp_dng_buffer->size ); + inp_dng_stream.SetReadPosition(0); + + if( read_dng( allocator, &inp_dng_stream, &raw_buffer, NULL ) == false ) + { + assert(0); return false; + } + + out_raw_buffer->buffer = allocator->Alloc( raw_buffer.get_size() ); + out_raw_buffer->size = raw_buffer.get_size(); + + memcpy(out_raw_buffer->buffer, raw_buffer.get_buffer(), raw_buffer.get_size() ); + + TIMESTAMP("[END]", 1) + + return true; +} + +//!< dng to raw conversion +bool gpr_convert_dng_to_dng(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_dng_buffer, + gpr_buffer* out_dng_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_buffer_auto raw_buffer(allocator->Alloc, allocator->Free); + + dng_memory_stream inp_dng_stream( gDefaultDNGMemoryAllocator ); + inp_dng_stream.Put( inp_dng_buffer->buffer, inp_dng_buffer->size ); + inp_dng_stream.SetReadPosition(0); + + if( read_dng( allocator, &inp_dng_stream, &raw_buffer, NULL ) == false ) + { + assert(0); return false; + } + + dng_memory_stream out_dng_stream( gDefaultDNGMemoryAllocator ); + + write_dng( allocator, &out_dng_stream, &raw_buffer, false, NULL, parameters ); + + write_dngstream_to_buffer( &out_dng_stream, out_dng_buffer, allocator->Alloc, allocator->Free ); + + TIMESTAMP("[END]", 1) + + return true; +} + +bool gpr_convert_vc5_to_gpr(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_vc5_buffer, + gpr_buffer* out_gpr_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_buffer_auto vc5_buffer(allocator->Alloc, allocator->Free); + vc5_buffer.set( (char*)inp_vc5_buffer->buffer, inp_vc5_buffer->size ); + + dng_memory_stream out_gpr_stream( gDefaultDNGMemoryAllocator ); + + write_dng( allocator, &out_gpr_stream, NULL, false, &vc5_buffer, parameters ); + + write_dngstream_to_buffer( &out_gpr_stream, out_gpr_buffer, allocator->Alloc, allocator->Free ); + + TIMESTAMP("[END]", 1) + + return true; +} + +bool gpr_convert_gpr_to_vc5(const gpr_allocator* allocator, + gpr_buffer* inp_gpr_buffer, + gpr_buffer* out_vc5_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_buffer_auto vc5_buffer(allocator->Alloc, allocator->Free); + + dng_memory_stream inp_gpr_stream( gDefaultDNGMemoryAllocator ); + inp_gpr_stream.Put( inp_gpr_buffer->buffer, inp_gpr_buffer->size ); + inp_gpr_stream.SetReadPosition(0); + + if( read_dng( allocator, &inp_gpr_stream, NULL, &vc5_buffer ) == false ) + { + assert(0); return false; + } + + if( vc5_buffer.is_valid() == false ) + { + return false; + } + + out_vc5_buffer->buffer = allocator->Alloc( vc5_buffer.get_size() ); + out_vc5_buffer->size = vc5_buffer.get_size(); + memcpy(out_vc5_buffer->buffer, vc5_buffer.get_buffer(), vc5_buffer.get_size() ); + + TIMESTAMP("[END]", 1) + + return true; +} + +#if GPR_WRITING + +bool gpr_convert_raw_to_gpr(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_raw_buffer, + gpr_buffer* out_gpr_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_buffer_auto raw_buffer(allocator->Alloc, allocator->Free); + + raw_buffer.set(inp_raw_buffer->buffer, inp_raw_buffer->size); + + dng_memory_stream out_gpr_stream( gDefaultDNGMemoryAllocator ); + + write_dng( allocator, &out_gpr_stream, &raw_buffer, true, NULL, parameters ); + + write_dngstream_to_buffer( &out_gpr_stream, out_gpr_buffer, allocator->Alloc, allocator->Free ); + + TIMESTAMP("[END]", 1) + + return true; +} + +bool gpr_convert_dng_to_gpr(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_dng_buffer, + gpr_buffer* out_gpr_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_buffer_auto raw_buffer(allocator->Alloc, allocator->Free); + + dng_memory_stream inp_dng_stream( gDefaultDNGMemoryAllocator ); + inp_dng_stream.Put( inp_dng_buffer->buffer, inp_dng_buffer->size ); + inp_dng_stream.SetReadPosition(0); + + if( read_dng( allocator, &inp_dng_stream, &raw_buffer, NULL, NULL ) == false ) + { + assert(0); return false; + } + + dng_memory_stream out_gpr_stream( gDefaultDNGMemoryAllocator ); + + write_dng( allocator, &out_gpr_stream, &raw_buffer, true, NULL, parameters ); + + write_dngstream_to_buffer( &out_gpr_stream, out_gpr_buffer, allocator->Alloc, allocator->Free ); + + TIMESTAMP("[END]", 1) + + return true; +} + +bool gpr_convert_dng_to_vc5(const gpr_allocator* allocator, + gpr_buffer* inp_dng_buffer, + gpr_buffer* out_vc5_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_buffer_auto raw_buffer(allocator->Alloc, allocator->Free); + gpr_buffer_auto vc5_buffer(allocator->Alloc, allocator->Free); + + dng_memory_stream inp_dng_stream( gDefaultDNGMemoryAllocator ); + inp_dng_stream.Put( inp_dng_buffer->buffer, inp_dng_buffer->size ); + inp_dng_stream.SetReadPosition(0); + + if( read_dng( allocator, &inp_dng_stream, &raw_buffer, NULL ) == false ) + { + assert(0); return false; + } + + out_vc5_buffer->buffer = allocator->Alloc( vc5_buffer.get_size() ); + out_vc5_buffer->size = vc5_buffer.get_size(); + + memcpy(out_vc5_buffer->buffer, vc5_buffer.get_buffer(), vc5_buffer.get_size() ); + + TIMESTAMP("[END]", 1) + + return true; +} + +#endif // GPR_WRITING + +#if GPR_READING + +bool gpr_convert_gpr_to_rgb(const gpr_allocator* allocator, + GPR_RGB_RESOLUTION rgb_resolution, + int rgb_bits, + gpr_buffer* inp_gpr_buffer, + gpr_rgb_buffer* out_rgb_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_parameters params; + + gpr_buffer_auto vc5_buffer(allocator->Alloc, allocator->Free); + + dng_memory_stream inp_gpr_stream( gDefaultDNGMemoryAllocator ); + inp_gpr_stream.Put( inp_gpr_buffer->buffer, inp_gpr_buffer->size ); + inp_gpr_stream.SetReadPosition(0); + + if( read_dng( allocator, &inp_gpr_stream, NULL, &vc5_buffer, ¶ms ) == false ) + { + assert(0); return false; + } + + if( vc5_buffer.is_valid() == false ) + { + return false; + } + + vc5_decoder_parameters vc5_decoder_params; + + vc5_decoder_parameters_set_default(&vc5_decoder_params); + + vc5_decoder_params.mem_alloc = allocator->Alloc; + vc5_decoder_params.mem_free = allocator->Free; + vc5_decoder_params.pixel_format = VC5_DECODER_PIXEL_FORMAT_DEFAULT; + + vc5_decoder_params.rgb_bits = rgb_bits; + + gpr_rgb_gain& rgb_gain = vc5_decoder_params.rgb_gain; + + find_rational( params.tuning_info.wb_gains.r_gain, 0.125, &rgb_gain.r_gain_num, &rgb_gain.r_gain_pow2_den ); + find_rational( params.tuning_info.wb_gains.g_gain, 0.125, &rgb_gain.g_gain_num, &rgb_gain.g_gain_pow2_den ); + find_rational( params.tuning_info.wb_gains.b_gain, 0.125, &rgb_gain.b_gain_num, &rgb_gain.b_gain_pow2_den ); + + vc5_decoder_params.rgb_resolution = rgb_resolution; + + if( vc5_decoder_process( &vc5_decoder_params, &vc5_buffer.get_gpr_buffer(), NULL, out_rgb_buffer ) != CODEC_ERROR_OKAY ) + { + assert(0); + } + + TIMESTAMP("[END]", 1) + + return true; +} + +bool gpr_convert_gpr_to_dng(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_gpr_buffer, + gpr_buffer* out_dng_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_buffer_auto raw_buffer(allocator->Alloc, allocator->Free); + gpr_buffer_auto vc5_buffer(allocator->Alloc, allocator->Free); + + dng_memory_stream inp_gpr_stream( gDefaultDNGMemoryAllocator ); + inp_gpr_stream.Put( inp_gpr_buffer->buffer, inp_gpr_buffer->size ); + inp_gpr_stream.SetReadPosition(0); + + if( read_dng( allocator, &inp_gpr_stream, &raw_buffer, &vc5_buffer, NULL ) == false ) + { + assert(0); return false; + } + + dng_memory_stream out_dng_stream( gDefaultDNGMemoryAllocator ); + + write_dng( allocator, &out_dng_stream, &raw_buffer, false, NULL, parameters ); + + write_dngstream_to_buffer( &out_dng_stream, out_dng_buffer, allocator->Alloc, allocator->Free ); + + TIMESTAMP("[END]", 1) + + return true; +} + +bool gpr_convert_vc5_to_dng(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_vc5_buffer, + gpr_buffer* out_dng_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_buffer_auto vc5_buffer( allocator->Alloc, allocator->Free ); + + vc5_buffer.set(inp_vc5_buffer->buffer, inp_vc5_buffer->size); + + dng_memory_stream out_dng_stream( gDefaultDNGMemoryAllocator ); + + write_dng( allocator, &out_dng_stream, NULL, false, &vc5_buffer, parameters ); + + write_dngstream_to_buffer( &out_dng_stream, out_dng_buffer, allocator->Alloc, allocator->Free ); + + TIMESTAMP("[END]", 1) + + return true; +} + +bool gpr_convert_gpr_to_raw(const gpr_allocator* allocator, + gpr_buffer* inp_gpr_buffer, + gpr_buffer* out_raw_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_buffer_auto raw_buffer(allocator->Alloc, allocator->Free); + + dng_memory_stream inp_gpr_stream( gDefaultDNGMemoryAllocator ); + inp_gpr_stream.Put( inp_gpr_buffer->buffer, inp_gpr_buffer->size ); + inp_gpr_stream.SetReadPosition(0); + + if( read_dng( allocator, &inp_gpr_stream, &raw_buffer, NULL ) == false ) + { + assert(0); return false; + } + + out_raw_buffer->buffer = allocator->Alloc( raw_buffer.get_size() ); + out_raw_buffer->size = raw_buffer.get_size(); + + memcpy(out_raw_buffer->buffer, raw_buffer.get_buffer(), raw_buffer.get_size() ); + + TIMESTAMP("[END]", 1) + + return true; +} + +#endif // GPR_READING + +bool gpr_check_vc5( const gpr_allocator* allocator, + gpr_buffer* inp_dng_buffer) +{ + TIMESTAMP("[BEG]", 1) + + gpr_buffer_auto raw_buffer(allocator->Alloc, allocator->Free); + gpr_buffer_auto vc5_buffer(allocator->Alloc, allocator->Free); + bool is_vc5_format = false; + + { + dng_memory_stream inp_dng_stream( gDefaultDNGMemoryAllocator ); + inp_dng_stream.Put( inp_dng_buffer->buffer, inp_dng_buffer->size ); + inp_dng_stream.SetReadPosition(0); + + if( read_dng( allocator, &inp_dng_stream, &raw_buffer, &vc5_buffer, NULL, &is_vc5_format ) == false ) + { + assert(0); return -1; + } + } + + TIMESTAMP("[END]", 1) + + return is_vc5_format; +} + + diff --git a/source/lib/gpr_sdk/private/gpr_exif_info.cpp b/source/lib/gpr_sdk/private/gpr_exif_info.cpp new file mode 100755 index 0000000..4b1e9bc --- /dev/null +++ b/source/lib/gpr_sdk/private/gpr_exif_info.cpp @@ -0,0 +1,105 @@ + +#include "gpr_platform.h" +#include "gpr_exif_info.h" +#include "stdc_includes.h" + +static gpr_signed_rational signed_rational_construct(int32_t numerator, int32_t denominator) +{ + gpr_signed_rational a; + a.numerator = numerator; + a.denominator = denominator; + return a; +} + +static gpr_unsigned_rational unsigned_rational_construct(int32_t numerator, int32_t denominator) +{ + gpr_unsigned_rational a; + a.numerator = numerator; + a.denominator = denominator; + return a; +} + +gpr_date_and_time construct_dng_date_and_time (uint32_t year, uint32_t month, uint32_t day, uint32_t hour, uint32_t minute, uint32_t second) +{ + gpr_date_and_time x; + x.year = year; + x.month = month; + x.day = day; + x.hour = hour; + x.minute = minute; + x.second = second; + + return x; +} + +void gpr_exif_info_set_defaults( gpr_exif_info* x ) +{ + x->exposure_time = unsigned_rational_construct(1, 60); + + x->exposure_bias = signed_rational_construct( 0, 1 ); + + double d_aperture = 2.8; + + x->f_stop_number = unsigned_rational_construct( (unsigned int)(d_aperture * 1000), 1000 ); + + // Convert aperture to APEX + double aperture_apex = log(d_aperture) / log(sqrt(2.0)); + + x->aperture = unsigned_rational_construct( (unsigned int)(aperture_apex * 1000), 1000 ); + + x->focal_length = unsigned_rational_construct(3, 1); + + x->digital_zoom = unsigned_rational_construct(1, 1); + + x->metering_mode = gpr_metering_mode_center_weighted_average; + + x->focal_length_in_35mm_film = 15; + + x->exposure_program = gpr_exposure_program_normal; + + x->light_source = gpr_light_source_auto; + + x->flash = gpr_flash_not_supported; + + x->sensing_method = gpr_sensing_method_chip_color_area; + + x->file_source = gpr_file_source_digital_still; + + x->scene_type = gpr_scene_type_directly_photographed; + + x->white_balance = gpr_white_balance_auto; + + x->exposure_mode = gpr_exposure_mode_auto; + + x->scene_capture_type = gpr_scene_capture_type_standard; + + x->gain_control = gpr_gain_control_normal; + + x->contrast = gpr_contrast_normal; + + x->saturation = gpr_saturation_normal; + + x->sharpness = gpr_sharpness_hard; + + x->iso_speed_rating = 232; + + x->date_time_original = construct_dng_date_and_time (2016, 03, 25, 15, 55, 23 ); + + x->date_time_digitized = x->date_time_original; + + strcpy( x->camera_make, "GoPro" ); + + strcpy( x->camera_model, "HERO6 Black" ); + + sprintf( x->software_version, "%d.%d.%d", GPR_VERSION_MAJOR, GPR_VERSION_MINOR, GPR_VERSION_REVISION ); + + strcpy( x->user_comment, "" ); +} + +void gpr_exif_info_get_camera_make_and_model( const gpr_exif_info* x, char camera_make_and_model[256] ) +{ + strcpy(camera_make_and_model, x->camera_make ); + strcpy(camera_make_and_model + strlen(camera_make_and_model), " " ); + strcpy(camera_make_and_model + strlen(camera_make_and_model), x->camera_model ); +} + diff --git a/source/lib/gpr_sdk/private/gpr_image_writer.cpp b/source/lib/gpr_sdk/private/gpr_image_writer.cpp new file mode 100755 index 0000000..891f66d --- /dev/null +++ b/source/lib/gpr_sdk/private/gpr_image_writer.cpp @@ -0,0 +1,129 @@ + +#include "gpr_image_writer.h" + +#include "dng_camera_profile.h" +#include "dng_color_space.h" +#include "dng_date_time.h" +#include "dng_exceptions.h" +#include "dng_file_stream.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_ifd.h" +#include "dng_image_writer.h" +#include "dng_info.h" +#include "dng_linearization_info.h" +#include "dng_mosaic_info.h" +#include "dng_negative.h" +#include "dng_preview.h" +#include "dng_render.h" +#include "dng_simple_image.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_xmp.h" +#include "dng_xmp_sdk.h" +#include "dng_memory_stream.h" +#include "dng_bottlenecks.h" + +#if GPR_WRITING +#include "vc5_encoder.h" +#endif + +#include "gpr_utils.h" + +#if GPR_WRITING + +gpr_image_writer::gpr_image_writer(const gpr_buffer_auto* raw_buffer, + unsigned int raw_buffer_width, + unsigned int raw_buffer_height, + size_t raw_buffer_pitch, + gpr_buffer_auto* vc5_buffer + ) : _vc5_buffer_obj( raw_buffer->get_malloc(), raw_buffer->get_free() ), + _vc5_buffer(vc5_buffer), + _raw_buffer(raw_buffer) +{ + if( _vc5_buffer == NULL ) + { + _vc5_buffer = &_vc5_buffer_obj; + } + + _rgb_thumbnail.buffer = NULL; + _rgb_thumbnail.size = 0; + + vc5_encoder_parameters_set_default(&vc5_encoder_params); +} + +gpr_image_writer::~gpr_image_writer() +{ + if( _rgb_thumbnail.buffer ) + { + assert( _rgb_thumbnail.size > 0 ); + + _raw_buffer->get_free()(_rgb_thumbnail.buffer); + _rgb_thumbnail.buffer = NULL; + _rgb_thumbnail.size = 0; + } +} + + +void gpr_image_writer::EncodeVc5Image() +{ + if( _vc5_buffer->is_valid() == false ) + { + + gpr_buffer raw_image = { _raw_buffer->get_buffer(), _raw_buffer->get_size() }; + gpr_buffer vc5_image = { _vc5_buffer->get_buffer(), _vc5_buffer->get_size() }; + + if( vc5_encoder_process( &vc5_encoder_params, &raw_image, &vc5_image, &_rgb_thumbnail ) != CODEC_ERROR_OKAY ) + { + assert(0); + } + + _vc5_buffer->set( vc5_image.buffer, vc5_image.size, true ); + } +} + +void gpr_image_writer::WriteImage (dng_host &host, + const dng_ifd &ifd, + dng_basic_tag_set &basic, + dng_stream &stream, + const dng_image &image, + uint32 fakeChannels) +{ + dng_image_writer::WriteImage ( host, ifd, basic, stream, image, fakeChannels); +} + +uint32 gpr_image_writer::CompressedBufferSize (const dng_ifd &ifd, uint32 uncompressedSize) +{ + if( ifd.fCompression == ccVc5 ) + { + return _vc5_buffer->get_size(); + } + else + { + return dng_image_writer::CompressedBufferSize( ifd, uncompressedSize ); + } +} + +void gpr_image_writer::WriteTile (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + const dng_image &image, + const dng_rect &tileArea, + uint32 fakeChannels, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer, + AutoPtr &tempBuffer) +{ + if( ifd.fCompression == ccVc5 ) + { + stream.Put( _vc5_buffer->get_buffer(), _vc5_buffer->get_size() ); + } + else + { + return dng_image_writer::WriteTile( host, ifd, stream, image, tileArea, fakeChannels, compressedBuffer, uncompressedBuffer, subTileBlockBuffer, tempBuffer ); + } +} + +#endif // GPR_WRITING diff --git a/source/lib/gpr_sdk/private/gpr_image_writer.h b/source/lib/gpr_sdk/private/gpr_image_writer.h new file mode 100755 index 0000000..bb07c60 --- /dev/null +++ b/source/lib/gpr_sdk/private/gpr_image_writer.h @@ -0,0 +1,87 @@ +/*! @file gpr_image_writer.h + * + * @brief Declaration of gpr_image_writer class + * + * GPR API can be invoked by simply including this header file. + * This file includes all other header files that are needed. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_IMAGE_WRITER +#define GPR_IMAGE_WRITER + +#include "dng_image_writer.h" +#include "gpr_buffer_auto.h" + +#if GPR_WRITING + +#include "vc5_encoder.h" + +class gpr_image_writer : public dng_image_writer +{ +private: + + gpr_buffer_auto _vc5_buffer_obj; + + gpr_rgb_buffer _rgb_thumbnail; + + gpr_buffer_auto* _vc5_buffer; + const gpr_buffer_auto* _raw_buffer; + + vc5_encoder_parameters vc5_encoder_params; + +public: + + void EncodeVc5Image(); + + const gpr_rgb_buffer& get_rgb_thumbnail() { return _rgb_thumbnail; } + + vc5_encoder_parameters& GetVc5EncoderParams() { return vc5_encoder_params; } + + gpr_image_writer(const gpr_buffer_auto* raw_buffer, + unsigned int raw_buffer_width, + unsigned int raw_buffer_height, + size_t raw_buffer_pitch, + gpr_buffer_auto* vc5_buffer = NULL ); + + ~gpr_image_writer(); + + void WriteImage (dng_host &host, + const dng_ifd &ifd, + dng_basic_tag_set &basic, + dng_stream &stream, + const dng_image &image, + uint32 fakeChannels); + + uint32 CompressedBufferSize (const dng_ifd &ifd, uint32 uncompressedSize); + + uint32 GetDefaultCompression() { return ccVc5; } + + void WriteTile (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + const dng_image &image, + const dng_rect &tileArea, + uint32 fakeChannels, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer, + AutoPtr &tempBuffer); + +}; + +#endif // GPR_WRITING + +#endif // GPR_IMAGE_WRITER diff --git a/source/lib/gpr_sdk/private/gpr_profile_info.cpp b/source/lib/gpr_sdk/private/gpr_profile_info.cpp new file mode 100755 index 0000000..93ad542 --- /dev/null +++ b/source/lib/gpr_sdk/private/gpr_profile_info.cpp @@ -0,0 +1,57 @@ + +#include "gpr_profile_info.h" +#include "stdc_includes.h" + +void gpr_profile_info_set_defaults(gpr_profile_info* x) +{ + x->compute_color_matrix = true; + + x->matrix_weighting = 1.0; + + x->illuminant1 = 3; // tungsten + x->illuminant2 = 23; // d50 + + x->wb1[0] = 1.339600; + x->wb1[1] = 1.00000; + x->wb1[2] = 2.780029; + + x->wb2[0] = 1.9036; + x->wb2[1] = 1.00000; + x->wb2[2] = 1.7483; + + { + const double cam_to_srgb[] = { 1.2963, -0.2025, -0.0939, -0.4789, 1.5728, -0.0939, -0.1007, -0.7605, 1.8612 }; + + x->cam_to_srgb_1[0][0] = cam_to_srgb[0]; + x->cam_to_srgb_1[0][1] = cam_to_srgb[1]; + x->cam_to_srgb_1[0][2] = cam_to_srgb[2]; + + x->cam_to_srgb_1[1][0] = cam_to_srgb[3]; + x->cam_to_srgb_1[1][1] = cam_to_srgb[4]; + x->cam_to_srgb_1[1][2] = cam_to_srgb[5]; + + x->cam_to_srgb_1[2][0] = cam_to_srgb[6]; + x->cam_to_srgb_1[2][1] = cam_to_srgb[7]; + x->cam_to_srgb_1[2][2] = cam_to_srgb[8]; + } + + { + const double cam_to_srgb[] = { 1.5580, -0.3019, -0.2561, -0.3023, 1.6328, -0.3305, -0.0365, -0.5127, 1.5492 }; + + x->cam_to_srgb_2[0][0] = cam_to_srgb[0]; + x->cam_to_srgb_2[0][1] = cam_to_srgb[1]; + x->cam_to_srgb_2[0][2] = cam_to_srgb[2]; + + x->cam_to_srgb_2[1][0] = cam_to_srgb[3]; + x->cam_to_srgb_2[1][1] = cam_to_srgb[4]; + x->cam_to_srgb_2[1][2] = cam_to_srgb[5]; + + x->cam_to_srgb_2[2][0] = cam_to_srgb[6]; + x->cam_to_srgb_2[2][1] = cam_to_srgb[7]; + x->cam_to_srgb_2[2][2] = cam_to_srgb[8]; + } + + memset( x->color_matrix_1, 0, sizeof(x->color_matrix_1) ); + memset( x->color_matrix_2, 0, sizeof(x->color_matrix_2) ); +} + diff --git a/source/lib/gpr_sdk/private/gpr_read_image.cpp b/source/lib/gpr_sdk/private/gpr_read_image.cpp new file mode 100755 index 0000000..84c2fe9 --- /dev/null +++ b/source/lib/gpr_sdk/private/gpr_read_image.cpp @@ -0,0 +1,129 @@ + +#include "gpr_read_image.h" + +#include "dng_camera_profile.h" +#include "dng_color_space.h" +#include "dng_date_time.h" +#include "dng_exceptions.h" +#include "dng_file_stream.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_ifd.h" +#include "dng_image_writer.h" +#include "dng_info.h" +#include "dng_linearization_info.h" +#include "dng_mosaic_info.h" +#include "dng_negative.h" +#include "dng_preview.h" +#include "dng_render.h" +#include "dng_simple_image.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_xmp.h" +#include "dng_xmp_sdk.h" +#include "dng_memory_stream.h" +#include "dng_bottlenecks.h" + +#include "gpr_utils.h" + +#if GPR_READING + +#include "vc5_decoder.h" + +static bool DecodeVC5(dng_image &image, gpr_buffer_auto& vc5_buffer, VC5_DECODER_PIXEL_FORMAT pixel_format ) +{ + gpr_buffer_auto raw_buffer( malloc, free ); + + vc5_decoder_parameters vc5_decoder_params; + + vc5_decoder_parameters_set_default(&vc5_decoder_params); + + vc5_decoder_params.mem_alloc = raw_buffer.get_malloc(); + vc5_decoder_params.mem_free = raw_buffer.get_free(); + vc5_decoder_params.pixel_format = pixel_format; + + gpr_buffer vc5_image = { vc5_buffer.get_buffer(), vc5_buffer.get_size() }; + gpr_buffer raw_image = { raw_buffer.get_buffer(), raw_buffer.get_size() }; + + if( vc5_decoder_process( &vc5_decoder_params, &vc5_image, &raw_image, NULL ) != CODEC_ERROR_OKAY ) + { + assert(0); + return false; + } + + raw_buffer.set( raw_image.buffer, raw_image.size, true ); + + dng_point size = image.Bounds().Size(); + + CopyBufferToRawImage( raw_buffer, size.h, image ); + + return true; +} + +gpr_read_image::gpr_read_image( gpr_buffer_auto* vc5_buffer ) : _vc5_buffer(vc5_buffer) +{ + fReadVC5 = true; + fDecodeVC5 = true; +} + +void gpr_read_image::ReadTile (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer) +{ + + if( ifd.fCompression == ccVc5 ) + { + if( GetReadVC5() && _vc5_buffer != NULL ) + { + _vc5_buffer->allocate( tileByteCount ); + + stream.Get(_vc5_buffer->get_buffer(), _vc5_buffer->get_size() ); + + if( GetDecodeVC5() == true ) + { + bool rggb_raw = (ifd.fCFAPattern[0][0] == 0) && (ifd.fCFAPattern[0][1] == 1) && (ifd.fCFAPattern[1][0] == 1) && (ifd.fCFAPattern[1][1] == 2); + + VC5_DECODER_PIXEL_FORMAT pixel_format; + + if( rggb_raw ) + { + pixel_format = VC5_DECODER_PIXEL_FORMAT_RGGB_14; + } + else + { + pixel_format = VC5_DECODER_PIXEL_FORMAT_GBRG_12; + } + + if( DecodeVC5( image, *_vc5_buffer, pixel_format ) ) + { + return; + } + } + } + } + else + { + dng_read_image::ReadTile (host, + ifd, + stream, + image, + tileArea, + plane, + planes, + tileByteCount, + compressedBuffer, + uncompressedBuffer, + subTileBlockBuffer); + } +} + +#endif diff --git a/source/lib/gpr_sdk/private/gpr_read_image.h b/source/lib/gpr_sdk/private/gpr_read_image.h new file mode 100755 index 0000000..c8aa534 --- /dev/null +++ b/source/lib/gpr_sdk/private/gpr_read_image.h @@ -0,0 +1,64 @@ +/*! @file gpr_read_image.h + * + * @brief Declaration of gpr_read_image class + * + * GPR API can be invoked by simply including this header file. + * This file includes all other header files that are needed. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_READ_IMAGE +#define GPR_READ_IMAGE + +#include "dng_read_image.h" +#include "gpr_buffer_auto.h" + +#if GPR_READING + +class gpr_read_image : public dng_read_image +{ +private: + gpr_buffer_auto* _vc5_buffer; + + bool fReadVC5; + + bool fDecodeVC5; + +public: + + gpr_read_image( gpr_buffer_auto* vc5_buffer = NULL ); + + void SetReadVC5(bool read_vc5) { fReadVC5 = read_vc5; } + bool GetReadVC5() { return fReadVC5; } + + void SetDecodeVC5(bool decode_vc5) { fDecodeVC5 = decode_vc5; } + bool GetDecodeVC5() { return fDecodeVC5; } + + void ReadTile (dng_host &host, + const dng_ifd &ifd, + dng_stream &stream, + dng_image &image, + const dng_rect &tileArea, + uint32 plane, + uint32 planes, + uint32 tileByteCount, + AutoPtr &compressedBuffer, + AutoPtr &uncompressedBuffer, + AutoPtr &subTileBlockBuffer); +}; + +#endif // GPR_READING + +#endif // GPR_READ_IMAGE diff --git a/source/lib/gpr_sdk/private/gpr_tuning_info.cpp b/source/lib/gpr_sdk/private/gpr_tuning_info.cpp new file mode 100755 index 0000000..45ff9f7 --- /dev/null +++ b/source/lib/gpr_sdk/private/gpr_tuning_info.cpp @@ -0,0 +1,84 @@ + +#include "gpr_buffer_auto.h" + +#include "gpr_tuning_info.h" + +static void _static_black_level_set_defaults(gpr_static_black_level* x) +{ + const int black = 0; + + x->r_black = black; + x->g_r_black = black; + x->g_b_black = black; + x->b_black = black; +} + +static void _ae_info_set_defaults(gpr_auto_exposure_info* x) +{ + x->iso_value = 228; + x->shutter_time = 34952; +} + +static void _dgain_saturation_level_set_defaults(gpr_saturation_level* x) +{ + const int32_t saturation_level = 16383; + + x->level_red = saturation_level; + x->level_green_even = saturation_level; + x->level_green_odd = saturation_level; + x->level_blue = saturation_level; +} + +static void _wb_gains_set_defaults(gpr_white_balance_gains* x) +{ + x->r_gain = (float_t)6273.0 / 4096.0; + x->g_gain = (float_t)4096.0 / 4096.0; + x->b_gain = (float_t)8371.0 / 4096.0; +} + +static void _gain_map_set_defaults( gpr_tuning_info* tuning_info ) +{ + tuning_info->gain_map.size = 0; + + tuning_info->gain_map.buffers[0] = NULL; + tuning_info->gain_map.buffers[1] = NULL; + tuning_info->gain_map.buffers[2] = NULL; + tuning_info->gain_map.buffers[3] = NULL; +} + +int32_t gpr_tuning_info_get_dgain_saturation_level(const gpr_tuning_info* x, GPR_RAW_CHANNEL channel) +{ + switch(channel) + { + case RAW_CHANNEL_RED: + return x->dgain_saturation_level.level_red; + case RAW_CHANNEL_GREEN_EVEN: + return x->dgain_saturation_level.level_green_even; + case RAW_CHANNEL_GREEN_ODD: + return x->dgain_saturation_level.level_green_odd; + case RAW_CHANNEL_BLUE: + return x->dgain_saturation_level.level_blue; + default: + assert(0); + return 0; + } +} + +void gpr_tuning_info_set_defaults( gpr_tuning_info* x ) +{ + x->orientation = ORIENTATION_DEFAULT; + + _static_black_level_set_defaults(&x->static_black_level); + + _dgain_saturation_level_set_defaults(&x->dgain_saturation_level); + + _wb_gains_set_defaults(&x->wb_gains); + + _ae_info_set_defaults(&x->ae_info); + + _gain_map_set_defaults( x ); + + x->pixel_format = PIXEL_FORMAT_RGGB_14; +} + + diff --git a/source/lib/gpr_sdk/private/gpr_utils.cpp b/source/lib/gpr_sdk/private/gpr_utils.cpp new file mode 100755 index 0000000..7672fde --- /dev/null +++ b/source/lib/gpr_sdk/private/gpr_utils.cpp @@ -0,0 +1,73 @@ + +#include "gpr_utils.h" + +#include "dng_camera_profile.h" +#include "dng_color_space.h" +#include "dng_date_time.h" +#include "dng_exceptions.h" +#include "dng_file_stream.h" +#include "dng_globals.h" +#include "dng_host.h" +#include "dng_ifd.h" +#include "dng_image_writer.h" +#include "dng_info.h" +#include "dng_linearization_info.h" +#include "dng_mosaic_info.h" +#include "dng_negative.h" +#include "dng_preview.h" +#include "dng_render.h" +#include "dng_simple_image.h" +#include "dng_tag_codes.h" +#include "dng_tag_types.h" +#include "dng_tag_values.h" +#include "dng_xmp.h" +#include "dng_xmp_sdk.h" +#include "dng_memory_stream.h" +#include "dng_bottlenecks.h" + +#include "gpr_platform.h" +#include "gpr_buffer_auto.h" + +void CopyRawImageToBuffer( const dng_image& raw_image, gpr_buffer_auto& buffer ) +{ + dng_point size = raw_image.Bounds().Size(); + + const int raw_image_size = size.h * size.v * 2; + + buffer.allocate( raw_image_size ); + + dng_pixel_buffer pixel_buffer; + + pixel_buffer.fArea = dng_rect(size.v, size.h); + pixel_buffer.fPlane = 0; + pixel_buffer.fPlanes = 1; + pixel_buffer.fRowStep = size.h; + pixel_buffer.fColStep = pixel_buffer.fPlanes; + pixel_buffer.fPlaneStep = 1; + pixel_buffer.fPixelType = ttShort; + pixel_buffer.fPixelSize = TagTypeSize(ttShort); + + pixel_buffer.fData = buffer.get_buffer(); + + raw_image.Get(pixel_buffer); +} + +void CopyBufferToRawImage( const gpr_buffer_auto& buffer, size_t stride, dng_image& raw_image ) +{ + dng_pixel_buffer pixel_buffer; + + dng_point size = raw_image.Bounds().Size(); + + pixel_buffer.fArea = dng_rect(size.v, size.h); + pixel_buffer.fPlane = 0; + pixel_buffer.fPlanes = 1; + pixel_buffer.fRowStep = stride; + pixel_buffer.fColStep = pixel_buffer.fPlanes; + pixel_buffer.fPlaneStep = 1; + pixel_buffer.fPixelType = ttShort; + pixel_buffer.fPixelSize = TagTypeSize(ttShort); + + pixel_buffer.fData = buffer.get_buffer(); + + raw_image.Put(pixel_buffer); +} diff --git a/source/lib/gpr_sdk/private/gpr_utils.h b/source/lib/gpr_sdk/private/gpr_utils.h new file mode 100755 index 0000000..45e30f3 --- /dev/null +++ b/source/lib/gpr_sdk/private/gpr_utils.h @@ -0,0 +1,40 @@ +/*! @file gpr_utils.h + * + * @brief Declaration of general gpr functions that belong in gpr library + * + * GPR API can be invoked by simply including this header file. + * This file includes all other header files that are needed. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_UTILS_H +#define GPR_UTILS_H + +#include "gpr_buffer_auto.h" +#include "dng_image.h" + +#ifdef __cplusplus +extern "C" { +#endif + + void CopyRawImageToBuffer( const dng_image& raw_image, gpr_buffer_auto& buffer ); + + void CopyBufferToRawImage( const gpr_buffer_auto& buffer, size_t buffer_stride, dng_image& raw_image ); + +#ifdef __cplusplus +} +#endif + +#endif // GPR_UTILS_H diff --git a/source/lib/gpr_sdk/public/gpr.h b/source/lib/gpr_sdk/public/gpr.h new file mode 100755 index 0000000..666e727 --- /dev/null +++ b/source/lib/gpr_sdk/public/gpr.h @@ -0,0 +1,169 @@ +/*! @file gpr.h + * + * @brief Declaration of the GPR-SDK objects and functions + * + * GPR API can be invoked by simply including this header file. + * This file includes all other header files that are needed. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_H +#define GPR_H + +#include "gpr_platform.h" +#include "gpr_exif_info.h" +#include "gpr_profile_info.h" +#include "gpr_tuning_info.h" +#include "gpr_allocator.h" +#include "gpr_buffer.h" +#include "gpr_rgb_buffer.h" + +#ifdef __cplusplus + extern "C" { +#endif + + typedef struct + { + gpr_buffer jpg_preview; /* Address to the memory location that this buffer points to */ + + unsigned int preview_width; /* Width of input source in pixels (only applies to raw input) */ + + unsigned int preview_height; /* Height of input source in pixels (only applies to raw input) */ + + } gpr_preview_image; + + typedef struct + { + unsigned int input_width; /* Width of input source in pixels (only applies to raw input) */ + + unsigned int input_height; /* Height of input source in pixels (only applies to raw input) */ + + unsigned int input_pitch; /* Pitch of input source in pixels (only applies to raw input) */ + + bool fast_encoding; + + bool compute_md5sum; + + gpr_buffer gpmf_payload; /* GPMF payload of image file */ + + gpr_preview_image preview_image; /* Preview JPG image */ + + bool enable_preview; + + gpr_exif_info exif_info; /* Exif info object */ + + gpr_profile_info profile_info; /* Camera color profile info object */ + + gpr_tuning_info tuning_info; /* Camera tuning info object */ + + } gpr_parameters; + + void gpr_parameters_set_defaults(gpr_parameters* x); + + void gpr_parameters_construct_copy(const gpr_parameters* y, gpr_parameters* x); + + void gpr_parameters_destroy(gpr_parameters* x, gpr_free mem_free); + + //!< Parse Metadata of DNG File and return in gpr_parameters struct + bool gpr_parse_metadata(const gpr_allocator* allocator, + gpr_buffer* inp_dng_buffer, + gpr_parameters* parameters); + + //!< CHECK IF DNG IS VC5 COMPRESSED + bool gpr_check_vc5( gpr_buffer* inp_dng_buffer, gpr_malloc mem_alloc, gpr_free mem_free ); + + //!< CONVERSION FUNCTIONS + + //!< raw to dng conversion + bool gpr_convert_raw_to_dng(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_raw_buffer, + gpr_buffer* out_dng_buffer); + + //!< dng to raw conversion + bool gpr_convert_dng_to_raw(const gpr_allocator* allocator, + gpr_buffer* inp_dng_buffer, + gpr_buffer* out_raw_buffer); + + //!< dng to raw conversion + bool gpr_convert_dng_to_dng(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_dng_buffer, + gpr_buffer* out_dng_buffer); + + //!< vc5 to gpr conversion + bool gpr_convert_vc5_to_gpr(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_vc5_buffer, + gpr_buffer* out_gpr_buffer); + + //!< gpr to vc5 conversion + bool gpr_convert_gpr_to_vc5(const gpr_allocator* allocator, + gpr_buffer* inp_gpr_buffer, + gpr_buffer* out_vc5_buffer); + +#if GPR_WRITING + + //!< raw to gpr conversion + bool gpr_convert_raw_to_gpr(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_raw_buffer, + gpr_buffer* out_gpr_buffer); + + //!< dng to gpr conversion + bool gpr_convert_dng_to_gpr(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_dng_buffer, + gpr_buffer* out_gpr_buffer); + + //!< dng to vc5 conversion + bool gpr_convert_dng_to_vc5(const gpr_allocator* allocator, + gpr_buffer* inp_dng_buffer, + gpr_buffer* out_vc5_buffer); +#endif // GPR_WRITING + + +#if GPR_READING + + //!< gpr to rgb conversion + bool gpr_convert_gpr_to_rgb(const gpr_allocator* allocator, + GPR_RGB_RESOLUTION rgb_resolution, + int rgb_bits, + gpr_buffer* inp_gpr_buffer, + gpr_rgb_buffer* out_rgb_buffer); + + //!< gpr to dng conversion + bool gpr_convert_gpr_to_dng(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_gpr_buffer, + gpr_buffer* out_dng_buffer); + + //!< vc5 to dng conversion + bool gpr_convert_vc5_to_dng(const gpr_allocator* allocator, + const gpr_parameters* parameters, + gpr_buffer* inp_vc5_buffer, + gpr_buffer* out_dng_buffer); + + //!< gpr to raw conversion + bool gpr_convert_gpr_to_raw(const gpr_allocator* allocator, + gpr_buffer* inp_gpr_buffer, + gpr_buffer* out_raw_buffer); +#endif + +#ifdef __cplusplus + } +#endif + +#endif // GPR_H diff --git a/source/lib/gpr_sdk/public/gpr_exif_info.h b/source/lib/gpr_sdk/public/gpr_exif_info.h new file mode 100755 index 0000000..2917b25 --- /dev/null +++ b/source/lib/gpr_sdk/public/gpr_exif_info.h @@ -0,0 +1,352 @@ +/*! @file gpr_exif_info.h + * + * @brief Declaration of gpr_exif_info object and associated functions + * + * GPR API can be invoked by simply including this header file. + * This file includes all other header files that are needed. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_EXIF_INFO +#define GPR_EXIF_INFO + +#define CAMERA_MAKE_SIZE 32 +#define CAMERA_MODEL_SIZE 32 +#define CAMERA_SERIAL_SIZE 32 +#define SOFTWARE_VERSION_SIZE 32 +#define USER_COMMENT_SIZE 64 +#define SATELLITES_USED_SIZE 32 +#define SURVEY_DATA_SIZE 32 +#define PROCESSING_METHOD_SIZE 32 +#define AREA_INFORMATION_SIZE 32 +#define IMAGE_DESCRIPTION_SIZE 32 + +#include "gpr_platform.h" + +#ifdef __cplusplus + extern "C" { +#endif + + typedef enum + { + gpr_sensing_method_chip_color_area = 2, + + } gpr_sensing_method; + + typedef enum + { + gpr_file_source_digital_still = 3, + + } gpr_file_source; + + typedef enum + { + gpr_scene_type_directly_photographed = 1, + + } gpr_scene_type; + + typedef enum + { + gpr_white_balance_auto = 0, + + gpr_white_balance_manual = 1, + + } gpr_white_balance; + + typedef enum + { + gpr_exposure_mode_auto = 0, + + gpr_exposure_mode_manual = 1, + + gpr_exposure_mode_auto_bracket = 2, + + } gpr_exposure_mode; + + typedef enum + { + gpr_scene_capture_type_standard = 0, + + gpr_scene_capture_type_landscape = 1, + + gpr_scene_capture_type_portrait = 2, + + gpr_scene_capture_type_night = 3, + + } gpr_scene_capture_type; + + typedef enum + { + gpr_contrast_normal = 0, + + } gpr_contrast; + + typedef enum + { + gpr_gain_control_normal = 0, + + } gpr_gain_control; + + typedef enum + { + gpr_saturation_normal = 0, + + } gpr_saturation; + + typedef enum + { + gpr_sharpness_normal = 0, + + gpr_sharpness_soft = 1, + + gpr_sharpness_hard = 2, + + } gpr_sharpness; + + typedef enum + { + gpr_flash_not_used = 0, + + gpr_flash_used = 1, + + gpr_flash_not_supported = 32, + + } gpr_flash; + + typedef enum + { + gpr_exposure_program_manual_control = 1, + + gpr_exposure_program_normal = 2, + + gpr_exposure_program_aperture_priority = 3, + + gpr_exposure_program_shutter_priority = 4, + + gpr_exposure_program_creative = 5, + + gpr_exposure_program_action = 6, + + gpr_exposure_program_portrait_mode = 7, + + gpr_exposure_program_landscape_mode = 8, + + } gpr_exposure_program; + + typedef enum + { + gpr_metering_mode_average = 1, + + gpr_metering_mode_center_weighted_average = 2, + + gpr_metering_mode_spot = 3, + + gpr_metering_mode_multi_spot = 4, + + gpr_metering_mode_multi_segment = 5, + + } gpr_metering_mode; + + typedef enum + { + gpr_light_source_auto = 0, + + gpr_light_source_daylight = 1, + + gpr_light_source_fuorescent = 2, + + gpr_light_source_tungsten = 3, + + } gpr_light_source; // See this link for more info: http://www.awaresystems.be/imaging/tiff/tifftags/privateifd/exif/lightsource.html + + /*****************************************************************************/ + + typedef struct + { + int32_t numerator; // Numerator + + int32_t denominator; // Denominator + + } gpr_signed_rational; + + typedef struct + { + uint32_t numerator; // Numerator + + uint32_t denominator; // Denominator + + } gpr_unsigned_rational; + + typedef struct + { + uint32_t year; + + uint32_t month; + + uint32_t day; + + uint32_t hour; + + uint32_t minute; + + uint32_t second; + + } gpr_date_and_time; + + typedef struct + { + bool gps_info_valid; + + uint32_t version_id; + + char latitude_ref[2]; + + gpr_unsigned_rational latitude[3]; + + char longitude_ref[2]; + + gpr_unsigned_rational longitude[3]; + + uint8_t altitude_ref; + + gpr_unsigned_rational altitude; + + gpr_unsigned_rational time_stamp[3]; + + char satellites[SATELLITES_USED_SIZE]; + + char status[2]; + + char measure_mode[2]; + + gpr_unsigned_rational dop; + + char speed_ref[2]; + + gpr_unsigned_rational speed; + + char track_ref[2]; + + gpr_unsigned_rational track; + + char img_direction_ref[2]; + + gpr_unsigned_rational img_direction; + + char map_datum[SURVEY_DATA_SIZE]; + + char dest_latitude_ref[2]; + + gpr_unsigned_rational dest_latitude[3]; + + char dest_longitude_ref[2]; + + gpr_unsigned_rational dest_longitude[3]; + + char dest_bearing_ref[2]; + + gpr_unsigned_rational dest_bearing; + + char dest_distance_ref[2]; + + gpr_unsigned_rational dest_distance; + + char processing_method[PROCESSING_METHOD_SIZE]; + + char area_information[AREA_INFORMATION_SIZE]; + + char date_stamp[11]; + + unsigned short differential; + + } gpr_gps_info; + + typedef struct + { + char camera_make[CAMERA_MAKE_SIZE]; + + char camera_model[CAMERA_MODEL_SIZE]; + + char camera_serial[CAMERA_SERIAL_SIZE]; + + char software_version[SOFTWARE_VERSION_SIZE]; + + char user_comment[USER_COMMENT_SIZE]; + + char image_description[IMAGE_DESCRIPTION_SIZE]; + + gpr_unsigned_rational exposure_time; /* exposure time (as fraction) */ + + gpr_unsigned_rational f_stop_number; /* f-stop number (as fraction) */ + + gpr_unsigned_rational aperture; /* aperture */ + + gpr_exposure_program exposure_program; + + uint16_t iso_speed_rating; + + gpr_date_and_time date_time_original; + + gpr_date_and_time date_time_digitized; + + gpr_signed_rational exposure_bias; + + gpr_metering_mode metering_mode; + + gpr_light_source light_source; + + gpr_flash flash; + + gpr_unsigned_rational focal_length; + + gpr_sharpness sharpness; + + uint16_t saturation; + + gpr_gain_control gain_control; + + gpr_contrast contrast; + + gpr_scene_capture_type scene_capture_type; + + gpr_exposure_mode exposure_mode; + + uint16_t focal_length_in_35mm_film; + + gpr_unsigned_rational digital_zoom; + + gpr_white_balance white_balance; + + gpr_scene_type scene_type; + + gpr_file_source file_source; + + gpr_sensing_method sensing_method; + + gpr_gps_info gps_info; + + } gpr_exif_info; + + void gpr_exif_info_set_defaults( gpr_exif_info* x ); + + void gpr_exif_info_get_camera_make_and_model( const gpr_exif_info* x, char camera_make_and_model[256] ); + + gpr_date_and_time construct_gpr_date_and_time (uint32_t year, uint32_t month, uint32_t day, uint32_t hour, uint32_t minute, uint32_t second); + +#ifdef __cplusplus + } +#endif + +#endif // GPR_EXIF_INFO diff --git a/source/lib/gpr_sdk/public/gpr_profile_info.h b/source/lib/gpr_sdk/public/gpr_profile_info.h new file mode 100755 index 0000000..5fc37b3 --- /dev/null +++ b/source/lib/gpr_sdk/public/gpr_profile_info.h @@ -0,0 +1,60 @@ +/*! @file gpr_profile_info.h + * + * @brief Declaration of gpr_profile_info object and associated functions + * + * GPR API can be invoked by simply including this header file. + * This file includes all other header files that are needed. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_PROFILE_INFO_H +#define GPR_PROFILE_INFO_H + +#include "gpr_platform.h" + +#ifdef __cplusplus + extern "C" { +#endif + + typedef double Matrix[3][3]; + + typedef struct + { + + bool compute_color_matrix; + + double matrix_weighting; + + double wb1[3]; + double wb2[3]; + + Matrix cam_to_srgb_1; + Matrix cam_to_srgb_2; + + Matrix color_matrix_1; + Matrix color_matrix_2; + + uint16_t illuminant1; + uint16_t illuminant2; + + } gpr_profile_info; + + void gpr_profile_info_set_defaults(gpr_profile_info* x); + +#ifdef __cplusplus + } +#endif + +#endif // GPR_PROFILE_INFO_H diff --git a/source/lib/gpr_sdk/public/gpr_tuning_info.h b/source/lib/gpr_sdk/public/gpr_tuning_info.h new file mode 100755 index 0000000..677d68d --- /dev/null +++ b/source/lib/gpr_sdk/public/gpr_tuning_info.h @@ -0,0 +1,149 @@ +/*! @file gpr_tuning_info.h + * + * @brief Declaration of gpr_tuning_info object and associated functions + * + * GPR API can be invoked by simply including this header file. + * This file includes all other header files that are needed. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef GPR_TUNING_INFO_H +#define GPR_TUNING_INFO_H + +#include "gpr_platform.h" + +#ifdef __cplusplus + extern "C" { +#endif + + typedef enum + { + RAW_CHANNEL_RED = 0, + + RAW_CHANNEL_GREEN_EVEN = 1, + + RAW_CHANNEL_GREEN_ODD = 2, + + RAW_CHANNEL_BLUE = 3, + + } GPR_RAW_CHANNEL; + + typedef enum + { + PIXEL_FORMAT_RGGB_12 = 0, // RGGB 12bit pixels packed into 16bits + + PIXEL_FORMAT_RGGB_12P = 1, // RGGB 12bit pixels packed into 12bits + + PIXEL_FORMAT_RGGB_14 = 2, // RGGB 14bit pixels packed into 16bits + + PIXEL_FORMAT_GBRG_12 = 3, // GBRG 12bit pixels packed into 16bits + + PIXEL_FORMAT_GBRG_12P = 4, // GBRG 12bit pixels packed into 12bits + + } GPR_PIXEL_FORMAT; + + typedef enum + { + ORIENTATION_NORMAL = 0, + + ORIENTATION_MIRROR = 4, + + ORIENTATION_DEFAULT = ORIENTATION_MIRROR, + + } GPR_ORIENTATION; + + typedef struct + { + int32_t r_black; + + int32_t g_r_black; + + int32_t g_b_black; + + int32_t b_black; + + } gpr_static_black_level; + + typedef struct + { + uint16_t iso_value; + + uint32_t shutter_time; + + } gpr_auto_exposure_info; + + typedef struct + { + int32_t level_red; + + int32_t level_green_even; + + int32_t level_green_odd; + + int32_t level_blue; + + } gpr_saturation_level; + + typedef struct + { + float_t r_gain; + + float_t g_gain; + + float_t b_gain; + + } gpr_white_balance_gains; + + typedef struct + { + char *buffers[4]; + + uint32_t size; + + } gpr_gain_map; + + typedef struct + { + GPR_ORIENTATION orientation; + + gpr_static_black_level static_black_level; + + gpr_saturation_level dgain_saturation_level; + + gpr_white_balance_gains wb_gains; + + gpr_auto_exposure_info ae_info; + + double noise_scale; + double noise_offset; + + double warp_red_coefficient; + double warp_blue_coefficient; + + gpr_gain_map gain_map; + + GPR_PIXEL_FORMAT pixel_format; + + } gpr_tuning_info; + + int32_t gpr_tuning_info_get_dgain_saturation_level(const gpr_tuning_info* x, GPR_RAW_CHANNEL channel); + + void gpr_tuning_info_set_defaults( gpr_tuning_info* x ); + +#ifdef __cplusplus + } +#endif + +#endif // GPR_TUNING_INFO_H diff --git a/source/lib/md5_lib/CMakeLists.txt b/source/lib/md5_lib/CMakeLists.txt new file mode 100644 index 0000000..5782609 --- /dev/null +++ b/source/lib/md5_lib/CMakeLists.txt @@ -0,0 +1,23 @@ +# library +set( LIB_NAME md5_lib ) + +# get source files +file( GLOB BASE_SRC_FILES "*.c" ) + +# get include files +file( GLOB BASE_INC_FILES "*.h" ) + +# get all source files +set( SRC_FILES ${BASE_SRC_FILES} ) + +# get all include files +set( INC_FILES ${BASE_INC_FILES} ) + + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/source/lib/md5_lib/md5.c b/source/lib/md5_lib/md5.c new file mode 100755 index 0000000..0aada26 --- /dev/null +++ b/source/lib/md5_lib/md5.c @@ -0,0 +1,257 @@ +/* + * This code implements the MD5 message-digest algorithm. The algorithm was + * written by Ron Rivest. This code was written by Colin Plumb in 1993, our + * understanding is that no copyright is claimed and that this code is in the + * public domain. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is functionally equivalent, + * + * To compute the message digest of a chunk of bytes, declare an MD5Context + * structure, pass it to MD5Init, call MD5Update as needed on buffers full of + * bytes, and then call MD5Final, which will fill a supplied 16-byte array with + * the digest. + */ + +#include +#include +#include "md5.h" + +//! \ingroup libMD5 +//! \{ + +static void MD5Transform(uint32_t buf[4], uint32_t const in[16]); + +#ifndef __BIG_ENDIAN__ +# define byteReverse(buf, len) /* Nothing */ +#else +void byteReverse(uint32_t *buf, unsigned len); +/* + * Note: this code is harmless on little-endian machines. + */ +void byteReverse(uint32_t *buf, unsigned len) +{ + uint32_t t; + do { + char* bytes = (char *) buf; + t = ((unsigned) bytes[3] << 8 | bytes[2]) << 16 | + ((unsigned) bytes[1] << 8 | bytes[0]); + *buf = t; + buf++; + } while (--len); +} +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ + +void MD5Init(context_md5_t *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(context_md5_t *ctx, unsigned char *buf, unsigned len) +{ + uint32_t t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((uint32_t) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = ctx->in.b8 + t; + + t = 64 - t; + if (len < t) { + memcpy(p, buf, len); + return; + } + memcpy(p, buf, t); + byteReverse(ctx->in.b32, 16); + MD5Transform(ctx->buf, ctx->in.b32); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + memcpy(ctx->in.b8, buf, 64); + byteReverse(ctx->in.b32, 16); + MD5Transform(ctx->buf, ctx->in.b32); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + memcpy(ctx->in.b8, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], context_md5_t *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in.b8 + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + memset(p, 0, count); + byteReverse(ctx->in.b32, 16); + MD5Transform(ctx->buf, ctx->in.b32); + + /* Now fill the next block with 56 bytes */ + memset(ctx->in.b8, 0, 56); + } else { + /* Pad block to 56 bytes */ + memset(p, 0, count - 8); + } + byteReverse(ctx->in.b32, 14); + + /* Append length in bits and transform */ + ctx->in.b32[14] = ctx->bits[0]; + ctx->in.b32[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, ctx->in.b32); + byteReverse((uint32_t *) ctx->buf, 4); + memcpy(digest, ctx->buf, 16); + + memset(ctx, 0, sizeof(* ctx)); /* In case it's sensitive */ + /* The original version of this code omitted the asterisk. In + effect, only the first part of ctx was wiped with zeros, not + the whole thing. Bug found by Derek Jones. Original line: */ + // memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(uint32_t buf[4], uint32_t const in[16]) +{ + register uint32_t a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} diff --git a/source/lib/md5_lib/md5.h b/source/lib/md5_lib/md5.h new file mode 100755 index 0000000..5adc20e --- /dev/null +++ b/source/lib/md5_lib/md5.h @@ -0,0 +1,60 @@ +/* The copyright in this software is being made available under the BSD + * License, included below. This software may be subject to other third party + * and contributor rights, including patent rights, and no such rights are + * granted under this license. + * + * Copyright (c) 2010-2015, ITU/ISO/IEC + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of the ITU/ISO/IEC nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF + * THE POSSIBILITY OF SUCH DAMAGE. + */ +#pragma once +#include + +//! \ingroup libMD5 +//! \{ + +#define MD5_DIGEST_SIZE 16 + +typedef struct _context_md5_t { + uint32_t buf[4]; + uint32_t bits[2]; + union { + unsigned char b8[64]; + uint32_t b32[16]; + } in; +} context_md5_t; + +#ifdef __cplusplus +extern "C" { +#endif +void MD5Init(context_md5_t *ctx); +void MD5Update(context_md5_t *ctx, unsigned char *buf, unsigned len); +void MD5Final(unsigned char digest[16], context_md5_t *ctx); +#ifdef __cplusplus +} +#endif + +//! \} diff --git a/source/lib/tiny_jpeg/CMakeLists.txt b/source/lib/tiny_jpeg/CMakeLists.txt new file mode 100644 index 0000000..9d14921 --- /dev/null +++ b/source/lib/tiny_jpeg/CMakeLists.txt @@ -0,0 +1,23 @@ +# library +set( LIB_NAME tiny_jpeg ) + +# get source files +file( GLOB BASE_SRC_FILES "*.c" ) + +# get include files +file( GLOB BASE_INC_FILES "*.h" ) + +# get all source files +set( SRC_FILES ${BASE_SRC_FILES} ) + +# get all include files +set( INC_FILES ${BASE_INC_FILES} ) + + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/source/lib/tiny_jpeg/jpeg.c b/source/lib/tiny_jpeg/jpeg.c new file mode 100644 index 0000000..f94ded4 --- /dev/null +++ b/source/lib/tiny_jpeg/jpeg.c @@ -0,0 +1,3 @@ + +#define TJE_IMPLEMENTATION +#include "tiny_jpeg.h" diff --git a/source/lib/tiny_jpeg/jpeg.h b/source/lib/tiny_jpeg/jpeg.h new file mode 100644 index 0000000..7995304 --- /dev/null +++ b/source/lib/tiny_jpeg/jpeg.h @@ -0,0 +1,27 @@ +/*! @file gpr.h + * + * @brief Declaration of the jpg encoding API + * + * GPR API can be invoked by simply including this header file. + * This file includes all other header files that are needed. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef JPEG_H +#define JPEG_H + +#include "tiny_jpeg.h" + +#endif // JPEG_H diff --git a/source/lib/tiny_jpeg/tiny_jpeg.h b/source/lib/tiny_jpeg/tiny_jpeg.h new file mode 100644 index 0000000..1ea51cc --- /dev/null +++ b/source/lib/tiny_jpeg/tiny_jpeg.h @@ -0,0 +1,1301 @@ +/** + * tiny_jpeg.h + * + * Tiny JPEG Encoder + * - Sergio Gonzalez + * + * This is a readable and simple single-header JPEG encoder. + * + * Features + * - Implements Baseline DCT JPEG compression. + * - No dynamic allocations. + * + * This library is coded in the spirit of the stb libraries and mostly follows + * the stb guidelines. + * + * It is written in C99. And depends on the C standard library. + * Works with C++11 + * + * + * ==== Thanks ==== + * + * AssociationSirius (Bug reports) + * Bernard van Gastel (Thread-safe defaults, BSD compilation) + * + * + * ==== License ==== + * + * This software is in the public domain. Where that dedication is not + * recognized, you are granted a perpetual, irrevocable license to copy and + * modify this file as you see fit. + * + */ + +// ============================================================ +// Usage +// ============================================================ +// Include "tiny_jpeg.h" to and use the public interface defined below. +// +// You *must* do: +// +// #define TJE_IMPLEMENTATION +// #include "tiny_jpeg.h" +// +// in exactly one of your C files to actually compile the implementation. + + +// Here is an example program that loads a bmp with stb_image and writes it +// with Tiny JPEG + +/* + +#define STB_IMAGE_IMPLEMENTATION +#include "stb_image.h" + + +#define TJE_IMPLEMENTATION +#include "tiny_jpeg.h" + + +int main() +{ + int width, height, num_components; + unsigned char* data = stbi_load("in.bmp", &width, &height, &num_components, 0); + if ( !data ) { + puts("Could not find file"); + return EXIT_FAILURE; + } + + if ( !tje_encode_to_file("out.jpg", width, height, num_components, data) ) { + fprintf(stderr, "Could not write JPEG\n"); + return EXIT_FAILURE; + } + + return EXIT_SUCCESS; +} + +*/ + + + +#ifdef __cplusplus +extern "C" +{ +#endif + +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmissing-field-initializers" // We use {0}, which will zero-out the struct. +#pragma GCC diagnostic ignored "-Wmissing-braces" +#pragma GCC diagnostic ignored "-Wpadded" +#endif + +// ============================================================ +// Public interface: +// ============================================================ + +#ifndef TJE_HEADER_GUARD +#define TJE_HEADER_GUARD + +// - tje_encode_to_file - +// +// Usage: +// Takes bitmap data and writes a JPEG-encoded image to disk. +// +// PARAMETERS +// dest_path: filename to which we will write. e.g. "out.jpg" +// width, height: image size in pixels +// num_components: 3 is RGB. 4 is RGBA. Those are the only supported values +// src_data: pointer to the pixel data. +// +// RETURN: +// 0 on error. 1 on success. + +int tje_encode_to_file(const char* dest_path, + const int width, + const int height, + const int num_components, + const unsigned char* src_data); + +// - tje_encode_to_file_at_quality - +// +// Usage: +// Takes bitmap data and writes a JPEG-encoded image to disk. +// +// PARAMETERS +// dest_path: filename to which we will write. e.g. "out.jpg" +// quality: 3: Highest. Compression varies wildly (between 1/3 and 1/20). +// 2: Very good quality. About 1/2 the size of 3. +// 1: Noticeable. About 1/6 the size of 3, or 1/3 the size of 2. +// width, height: image size in pixels +// num_components: 3 is RGB. 4 is RGBA. Those are the only supported values +// src_data: pointer to the pixel data. +// +// RETURN: +// 0 on error. 1 on success. + +int tje_encode_to_file_at_quality(const char* dest_path, + const int quality, + const int width, + const int height, + const int num_components, + const unsigned char* src_data); + +// - tje_encode_with_func - +// +// Usage +// Same as tje_encode_to_file_at_quality, but it takes a callback that knows +// how to handle (or ignore) `context`. The callback receives an array `data` +// of `size` bytes, which can be written directly to a file. There is no need +// to free the data. + +typedef void tje_write_func(void* context, void* data, int size); + +int tje_encode_with_func(tje_write_func* func, + void* context, + const int quality, + const int width, + const int height, + const int num_components, + const unsigned char* src_data); + +#endif // TJE_HEADER_GUARD + + + +// Implementation: In exactly one of the source files of your application, +// define TJE_IMPLEMENTATION and include tiny_jpeg.h + +// ============================================================ +// Internal +// ============================================================ +#ifdef TJE_IMPLEMENTATION + +#define tjei_min(a, b) ((a) < b) ? (a) : (b); +#define tjei_max(a, b) ((a) < b) ? (b) : (a); + + +#if defined(_MSC_VER) +#define TJEI_FORCE_INLINE __forceinline +// #define TJEI_FORCE_INLINE __declspec(noinline) // For profiling +#else +#define TJEI_FORCE_INLINE static // TODO: equivalent for gcc & clang +#endif + +// Only use zero for debugging and/or inspection. +#define TJE_USE_FAST_DCT 1 + +// C std lib +#include +#include +#include // floorf, ceilf +#include // FILE, puts +#include // memcpy + + +#define TJEI_BUFFER_SIZE 1024 + +#ifdef _WIN32 + +#include +#ifndef snprintf +#define snprintf sprintf_s +#endif +// Not quite the same but it works for us. If I am not mistaken, it differs +// only in the return value. + +#endif + +#ifndef NDEBUG + +#ifdef _WIN32 +#define tje_log(msg) OutputDebugStringA(msg) +#elif defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) +#define tje_log(msg) puts(msg) +#else +#warning "need a tje_log definition for your platform for debugging purposes (not needed if compiling with NDEBUG)" +#endif + +#else // NDEBUG +#define tje_log(msg) +#endif // NDEBUG + + +typedef struct +{ + void* context; + tje_write_func* func; +} TJEWriteContext; + +typedef struct +{ + // Huffman data. + uint8_t ehuffsize[4][257]; + uint16_t ehuffcode[4][256]; + uint8_t const * ht_bits[4]; + uint8_t const * ht_vals[4]; + + // Cuantization tables. + uint8_t qt_luma[64]; + uint8_t qt_chroma[64]; + + // fwrite by default. User-defined when using tje_encode_with_func. + TJEWriteContext write_context; + + // Buffered output. Big performance win when using the usual stdlib implementations. + size_t output_buffer_count; + uint8_t output_buffer[TJEI_BUFFER_SIZE]; +} TJEState; + +// ============================================================ +// Table definitions. +// +// The spec defines tjei_default reasonably good quantization matrices and huffman +// specification tables. +// +// +// Instead of hard-coding the final huffman table, we only hard-code the table +// spec suggested by the specification, and then derive the full table from +// there. This is only for didactic purposes but it might be useful if there +// ever is the case that we need to swap huffman tables from various sources. +// ============================================================ + + +// K.1 - suggested luminance QT +static const uint8_t tjei_default_qt_luma_from_spec[] = +{ + 16,11,10,16, 24, 40, 51, 61, + 12,12,14,19, 26, 58, 60, 55, + 14,13,16,24, 40, 57, 69, 56, + 14,17,22,29, 51, 87, 80, 62, + 18,22,37,56, 68,109,103, 77, + 24,35,55,64, 81,104,113, 92, + 49,64,78,87,103,121,120,101, + 72,92,95,98,112,100,103, 99, +}; + +// Unused +#if 0 +static const uint8_t tjei_default_qt_chroma_from_spec[] = +{ + // K.1 - suggested chrominance QT + 17,18,24,47,99,99,99,99, + 18,21,26,66,99,99,99,99, + 24,26,56,99,99,99,99,99, + 47,66,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99, + 99,99,99,99,99,99,99,99, +}; +#endif + +static const uint8_t tjei_default_qt_chroma_from_paper[] = +{ + // Example QT from JPEG paper + 16, 12, 14, 14, 18, 24, 49, 72, + 11, 10, 16, 24, 40, 51, 61, 12, + 13, 17, 22, 35, 64, 92, 14, 16, + 22, 37, 55, 78, 95, 19, 24, 29, + 56, 64, 87, 98, 26, 40, 51, 68, + 81, 103, 112, 58, 57, 87, 109, 104, + 121,100, 60, 69, 80, 103, 113, 120, + 103, 55, 56, 62, 77, 92, 101, 99, +}; + +// == Procedure to 'deflate' the huffman tree: JPEG spec, C.2 + +// Number of 16 bit values for every code length. (K.3.3.1) +static const uint8_t tjei_default_ht_luma_dc_len[16] = +{ + 0,1,5,1,1,1,1,1,1,0,0,0,0,0,0,0 +}; +// values +static const uint8_t tjei_default_ht_luma_dc[12] = +{ + 0,1,2,3,4,5,6,7,8,9,10,11 +}; + +// Number of 16 bit values for every code length. (K.3.3.1) +static const uint8_t tjei_default_ht_chroma_dc_len[16] = +{ + 0,3,1,1,1,1,1,1,1,1,1,0,0,0,0,0 +}; +// values +static const uint8_t tjei_default_ht_chroma_dc[12] = +{ + 0,1,2,3,4,5,6,7,8,9,10,11 +}; + +// Same as above, but AC coefficients. +static const uint8_t tjei_default_ht_luma_ac_len[16] = +{ + 0,2,1,3,3,2,4,3,5,5,4,4,0,0,1,0x7d +}; +static const uint8_t tjei_default_ht_luma_ac[] = +{ + 0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07, + 0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0, + 0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28, + 0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, + 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, + 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, + 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5, + 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2, + 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, + 0xF9, 0xFA +}; + +static const uint8_t tjei_default_ht_chroma_ac_len[16] = +{ + 0,2,1,2,4,4,3,4,7,5,4,4,0,1,2,0x77 +}; +static const uint8_t tjei_default_ht_chroma_ac[] = +{ + 0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71, + 0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0, + 0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26, + 0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, + 0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, + 0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, + 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, + 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, + 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, + 0xF9, 0xFA +}; + + +// ============================================================ +// Code +// ============================================================ + +// Zig-zag order: +static const uint8_t tjei_zig_zag[64] = +{ + 0, 1, 5, 6, 14, 15, 27, 28, + 2, 4, 7, 13, 16, 26, 29, 42, + 3, 8, 12, 17, 25, 30, 41, 43, + 9, 11, 18, 24, 31, 40, 44, 53, + 10, 19, 23, 32, 39, 45, 52, 54, + 20, 22, 33, 38, 46, 51, 55, 60, + 21, 34, 37, 47, 50, 56, 59, 61, + 35, 36, 48, 49, 57, 58, 62, 63, +}; + +// Memory order as big endian. 0xhilo -> 0xlohi which looks as 0xhilo in memory. +static uint16_t tjei_be_word(const uint16_t le_word) +{ + uint16_t lo = (le_word & 0x00ff); + uint16_t hi = ((le_word & 0xff00) >> 8); + return (uint16_t)((lo << 8) | hi); +} + +// ============================================================ +// The following structs exist only for code clarity, debugability, and +// readability. They are used when writing to disk, but it is useful to have +// 1-packed-structs to document how the format works, and to inspect memory +// while developing. +// ============================================================ + +static const uint8_t tjeik_jfif_id[] = "JFIF"; +static const uint8_t tjeik_com_str[] = "Created by Tiny JPEG Encoder"; + +// TODO: Get rid of packed structs! +#pragma pack(push) +#pragma pack(1) +typedef struct +{ + uint16_t SOI; + // JFIF header. + uint16_t APP0; + uint16_t jfif_len; + uint8_t jfif_id[5]; + uint16_t version; + uint8_t units; + uint16_t x_density; + uint16_t y_density; + uint8_t x_thumb; + uint8_t y_thumb; +} TJEJPEGHeader; + +typedef struct +{ + uint16_t com; + uint16_t com_len; + char com_str[sizeof(tjeik_com_str) - 1]; +} TJEJPEGComment; + +// Helper struct for TJEFrameHeader (below). +typedef struct +{ + uint8_t component_id; + uint8_t sampling_factors; // most significant 4 bits: horizontal. 4 LSB: vertical (A.1.1) + uint8_t qt; // Quantization table selector. +} TJEComponentSpec; + +typedef struct +{ + uint16_t SOF; + uint16_t len; // 8 + 3 * frame.num_components + uint8_t precision; // Sample precision (bits per sample). + uint16_t height; + uint16_t width; + uint8_t num_components; // For this implementation, will be equal to 3. + TJEComponentSpec component_spec[3]; +} TJEFrameHeader; + +typedef struct +{ + uint8_t component_id; // Just as with TJEComponentSpec + uint8_t dc_ac; // (dc|ac) +} TJEFrameComponentSpec; + +typedef struct +{ + uint16_t SOS; + uint16_t len; + uint8_t num_components; // 3. + TJEFrameComponentSpec component_spec[3]; + uint8_t first; // 0 + uint8_t last; // 63 + uint8_t ah_al; // o +} TJEScanHeader; +#pragma pack(pop) + + +static void tjei_write(TJEState* state, const void* data, size_t num_bytes, size_t num_elements) +{ + size_t to_write = num_bytes * num_elements; + + // Cap to the buffer available size and copy memory. + size_t capped_count = tjei_min(to_write, TJEI_BUFFER_SIZE - 1 - state->output_buffer_count); + + memcpy(state->output_buffer + state->output_buffer_count, data, capped_count); + state->output_buffer_count += capped_count; + + assert (state->output_buffer_count <= TJEI_BUFFER_SIZE - 1); + + // Flush the buffer. + if ( state->output_buffer_count == TJEI_BUFFER_SIZE - 1 ) { + state->write_context.func(state->write_context.context, state->output_buffer, (int)state->output_buffer_count); + state->output_buffer_count = 0; + } + + // Recursively calling ourselves with the rest of the buffer. + if (capped_count < to_write) { + tjei_write(state, (uint8_t*)data+capped_count, to_write - capped_count, 1); + } +} + +static void tjei_write_DQT(TJEState* state, const uint8_t* matrix, uint8_t id) +{ + uint16_t DQT = tjei_be_word(0xffdb); + tjei_write(state, &DQT, sizeof(uint16_t), 1); + uint16_t len = tjei_be_word(0x0043); // 2(len) + 1(id) + 64(matrix) = 67 = 0x43 + tjei_write(state, &len, sizeof(uint16_t), 1); + assert(id < 4); + uint8_t precision_and_id = id; // 0x0000 8 bits | 0x00id + tjei_write(state, &precision_and_id, sizeof(uint8_t), 1); + // Write matrix + tjei_write(state, matrix, 64*sizeof(uint8_t), 1); +} + +typedef enum +{ + TJEI_DC = 0, + TJEI_AC = 1 +} TJEHuffmanTableClass; + +static void tjei_write_DHT(TJEState* state, + uint8_t const * matrix_len, + uint8_t const * matrix_val, + TJEHuffmanTableClass ht_class, + uint8_t id) +{ + int num_values = 0; + int i; + + for ( i = 0; i < 16; ++i ) { + num_values += matrix_len[i]; + } + assert(num_values <= 0xffff); + + uint16_t DHT = tjei_be_word(0xffc4); + // 2(len) + 1(Tc|th) + 16 (num lengths) + ?? (num values) + uint16_t len = tjei_be_word(2 + 1 + 16 + (uint16_t)num_values); + assert(id < 4); + uint8_t tc_th = (uint8_t)((((uint8_t)ht_class) << 4) | id); + + tjei_write(state, &DHT, sizeof(uint16_t), 1); + tjei_write(state, &len, sizeof(uint16_t), 1); + tjei_write(state, &tc_th, sizeof(uint8_t), 1); + tjei_write(state, matrix_len, sizeof(uint8_t), 16); + tjei_write(state, matrix_val, sizeof(uint8_t), (size_t)num_values); +} +// ============================================================ +// Huffman deflation code. +// ============================================================ + +// Returns all code sizes from the BITS specification (JPEG C.3) +static uint8_t* tjei_huff_get_code_lengths(uint8_t huffsize[/*256*/], uint8_t const * bits) +{ + int k = 0; + int i, j; + + for ( i = 0; i < 16; ++i ) { + for ( j = 0; j < bits[i]; ++j ) { + huffsize[k++] = (uint8_t)(i + 1); + } + huffsize[k] = 0; + } + return huffsize; +} + +// Fills out the prefixes for each code. +static uint16_t* tjei_huff_get_codes(uint16_t codes[], uint8_t* huffsize, int64_t count) +{ + uint16_t code = 0; + int k = 0; + uint8_t sz = huffsize[0]; + for(;;) { + do { + assert(k < count); + codes[k++] = code++; + } while (huffsize[k] == sz); + if (huffsize[k] == 0) { + return codes; + } + do { + code = (uint16_t)(code << 1); + ++sz; + } while( huffsize[k] != sz ); + } +} + +static void tjei_huff_get_extended(uint8_t* out_ehuffsize, + uint16_t* out_ehuffcode, + uint8_t const * huffval, + uint8_t* huffsize, + uint16_t* huffcode, int64_t count) +{ + int k = 0; + do { + uint8_t val = huffval[k]; + out_ehuffcode[val] = huffcode[k]; + out_ehuffsize[val] = huffsize[k]; + k++; + } while ( k < count ); +} +// ============================================================ + +// Returns: +// out[1] : number of bits +// out[0] : bits +TJEI_FORCE_INLINE void tjei_calculate_variable_length_int(int value, uint16_t out[2]) +{ + int abs_val = value; + if ( value < 0 ) { + abs_val = -abs_val; + --value; + } + out[1] = 1; + while( abs_val >>= 1 ) { + ++out[1]; + } + out[0] = (uint16_t)(value & ((1 << out[1]) - 1)); +} + +// Write bits to file. +TJEI_FORCE_INLINE void tjei_write_bits(TJEState* state, + uint32_t* bitbuffer, uint32_t* location, + uint16_t num_bits, uint16_t bits) +{ + // v-- location + // [ ] <-- bit buffer + // 32 0 + // + // This call pushes to the bitbuffer and saves the location. Data is pushed + // from most significant to less significant. + // When we can write a full byte, we write a byte and shift. + + // Push the stack. + uint32_t nloc = *location + num_bits; + *bitbuffer |= (uint32_t)(bits << (32 - nloc)); + *location = nloc; + while ( *location >= 8 ) { + // Grab the most significant byte. + uint8_t c = (uint8_t)((*bitbuffer) >> 24); + // Write it to file. + tjei_write(state, &c, 1, 1); + if ( c == 0xff ) { + // Special case: tell JPEG this is not a marker. + char z = 0; + tjei_write(state, &z, 1, 1); + } + // Pop the stack. + *bitbuffer <<= 8; + *location -= 8; + } +} + +// DCT implementation by Thomas G. Lane. +// Obtained through NVIDIA +// http://developer.download.nvidia.com/SDK/9.5/Samples/vidimaging_samples.html#gpgpu_dct +// +// QUOTE: +// This implementation is based on Arai, Agui, and Nakajima's algorithm for +// scaled DCT. Their original paper (Trans. IEICE E-71(11):1095) is in +// Japanese, but the algorithm is described in the Pennebaker & Mitchell +// JPEG textbook (see REFERENCES section in file README). The following code +// is based directly on figure 4-8 in P&M. +// +static void tjei_fdct (float * data) +{ + float tmp0, tmp1, tmp2, tmp3, tmp4, tmp5, tmp6, tmp7; + float tmp10, tmp11, tmp12, tmp13; + float z1, z2, z3, z4, z5, z11, z13; + float *dataptr; + int ctr; + + /* Pass 1: process rows. */ + + dataptr = data; + for ( ctr = 7; ctr >= 0; ctr-- ) { + tmp0 = dataptr[0] + dataptr[7]; + tmp7 = dataptr[0] - dataptr[7]; + tmp1 = dataptr[1] + dataptr[6]; + tmp6 = dataptr[1] - dataptr[6]; + tmp2 = dataptr[2] + dataptr[5]; + tmp5 = dataptr[2] - dataptr[5]; + tmp3 = dataptr[3] + dataptr[4]; + tmp4 = dataptr[3] - dataptr[4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[0] = tmp10 + tmp11; /* phase 3 */ + dataptr[4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((float) 0.707106781); /* c4 */ + dataptr[2] = tmp13 + z1; /* phase 5 */ + dataptr[6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((float) 0.382683433); /* c6 */ + z2 = ((float) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((float) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((float) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[5] = z13 + z2; /* phase 6 */ + dataptr[3] = z13 - z2; + dataptr[1] = z11 + z4; + dataptr[7] = z11 - z4; + + dataptr += 8; /* advance pointer to next row */ + } + + /* Pass 2: process columns. */ + + dataptr = data; + for ( ctr = 8-1; ctr >= 0; ctr-- ) { + tmp0 = dataptr[8*0] + dataptr[8*7]; + tmp7 = dataptr[8*0] - dataptr[8*7]; + tmp1 = dataptr[8*1] + dataptr[8*6]; + tmp6 = dataptr[8*1] - dataptr[8*6]; + tmp2 = dataptr[8*2] + dataptr[8*5]; + tmp5 = dataptr[8*2] - dataptr[8*5]; + tmp3 = dataptr[8*3] + dataptr[8*4]; + tmp4 = dataptr[8*3] - dataptr[8*4]; + + /* Even part */ + + tmp10 = tmp0 + tmp3; /* phase 2 */ + tmp13 = tmp0 - tmp3; + tmp11 = tmp1 + tmp2; + tmp12 = tmp1 - tmp2; + + dataptr[8*0] = tmp10 + tmp11; /* phase 3 */ + dataptr[8*4] = tmp10 - tmp11; + + z1 = (tmp12 + tmp13) * ((float) 0.707106781); /* c4 */ + dataptr[8*2] = tmp13 + z1; /* phase 5 */ + dataptr[8*6] = tmp13 - z1; + + /* Odd part */ + + tmp10 = tmp4 + tmp5; /* phase 2 */ + tmp11 = tmp5 + tmp6; + tmp12 = tmp6 + tmp7; + + /* The rotator is modified from fig 4-8 to avoid extra negations. */ + z5 = (tmp10 - tmp12) * ((float) 0.382683433); /* c6 */ + z2 = ((float) 0.541196100) * tmp10 + z5; /* c2-c6 */ + z4 = ((float) 1.306562965) * tmp12 + z5; /* c2+c6 */ + z3 = tmp11 * ((float) 0.707106781); /* c4 */ + + z11 = tmp7 + z3; /* phase 5 */ + z13 = tmp7 - z3; + + dataptr[8*5] = z13 + z2; /* phase 6 */ + dataptr[8*3] = z13 - z2; + dataptr[8*1] = z11 + z4; + dataptr[8*7] = z11 - z4; + + dataptr++; /* advance pointer to next column */ + } +} +#if !TJE_USE_FAST_DCT +static float slow_fdct(int u, int v, float* data) +{ +#define kPI 3.14159265f + int x, y; + float res = 0.0f; + float cu = (u == 0) ? 0.70710678118654f : 1; + float cv = (v == 0) ? 0.70710678118654f : 1; + for ( y = 0; y < 8; ++y ) { + for ( x = 0; x < 8; ++x ) { + res += (data[y * 8 + x]) * + cosf(((2.0f * x + 1.0f) * u * kPI) / 16.0f) * + cosf(((2.0f * y + 1.0f) * v * kPI) / 16.0f); + } + } + res *= 0.25f * cu * cv; + return res; +#undef kPI +} +#endif + +#define ABS(x) ((x) < 0 ? -(x) : (x)) + +static void tjei_encode_and_write_MCU(TJEState* state, + float* mcu, +#if TJE_USE_FAST_DCT + float* qt, // Pre-processed quantization matrix. +#else + uint8_t* qt, +#endif + uint8_t* huff_dc_len, uint16_t* huff_dc_code, // Huffman tables + uint8_t* huff_ac_len, uint16_t* huff_ac_code, + int* pred, // Previous DC coefficient + uint32_t* bitbuffer, // Bitstack. + uint32_t* location) +{ + int du[64]; // Data unit in zig-zag order + + float dct_mcu[64]; + memcpy(dct_mcu, mcu, 64 * sizeof(float)); + +#if TJE_USE_FAST_DCT + tjei_fdct(dct_mcu); + int i; + + for ( i = 0; i < 64; ++i ) { + float fval = dct_mcu[i]; + fval *= qt[i]; +#if 0 + fval = (fval > 0) ? floorf(fval + 0.5f) : ceilf(fval - 0.5f); +#else + fval = floorf(fval + 1024 + 0.5f); + fval -= 1024; +#endif + int val = (int)fval; + du[tjei_zig_zag[i]] = val; + } +#else + int u, v; + + for ( v = 0; v < 8; ++v ) { + for ( u = 0; u < 8; ++u ) { + dct_mcu[v * 8 + u] = slow_fdct(u, v, mcu); + } + } + + for ( i = 0; i < 64; ++i ) { + float fval = dct_mcu[i] / (qt[i]); + int val = (int)((fval > 0) ? floorf(fval + 0.5f) : ceilf(fval - 0.5f)); + du[tjei_zig_zag[i]] = val; + } +#endif + + uint16_t vli[2]; + + // Encode DC coefficient. + int diff = du[0] - *pred; + *pred = du[0]; + if ( diff != 0 ) { + tjei_calculate_variable_length_int(diff, vli); + // Write number of bits with Huffman coding + tjei_write_bits(state, bitbuffer, location, huff_dc_len[vli[1]], huff_dc_code[vli[1]]); + // Write the bits. + tjei_write_bits(state, bitbuffer, location, vli[1], vli[0]); + } else { + tjei_write_bits(state, bitbuffer, location, huff_dc_len[0], huff_dc_code[0]); + } + + // ==== Encode AC coefficients ==== + + int last_non_zero_i = 0; + + // Find the last non-zero element. + for ( i = 63; i > 0; --i ) { + if (du[i] != 0) { + last_non_zero_i = i; + break; + } + } + + for ( i = 1; i <= last_non_zero_i; ++i ) { + // If zero, increase count. If >=15, encode (FF,00) + int zero_count = 0; + while ( du[i] == 0 ) { + ++zero_count; + ++i; + if (zero_count == 16) { + // encode (ff,00) == 0xf0 + tjei_write_bits(state, bitbuffer, location, huff_ac_len[0xf0], huff_ac_code[0xf0]); + zero_count = 0; + } + } + tjei_calculate_variable_length_int(du[i], vli); + + assert(zero_count < 0x10); + assert(vli[1] <= 10); + + uint16_t sym1 = (uint16_t)((uint16_t)zero_count << 4) | vli[1]; + + assert(huff_ac_len[sym1] != 0); + + // Write symbol 1 --- (RUNLENGTH, SIZE) + tjei_write_bits(state, bitbuffer, location, huff_ac_len[sym1], huff_ac_code[sym1]); + // Write symbol 2 --- (AMPLITUDE) + tjei_write_bits(state, bitbuffer, location, vli[1], vli[0]); + } + + if (last_non_zero_i != 63) { + // write EOB HUFF(00,00) + tjei_write_bits(state, bitbuffer, location, huff_ac_len[0], huff_ac_code[0]); + } + return; +} + +enum { + TJEI_LUMA_DC, + TJEI_LUMA_AC, + TJEI_CHROMA_DC, + TJEI_CHROMA_AC, +}; + +#if TJE_USE_FAST_DCT +struct TJEProcessedQT +{ + float chroma[64]; + float luma[64]; +}; +#endif + +// Set up huffman tables in state. +static void tjei_huff_expand(TJEState* state) +{ + assert(state); + + state->ht_bits[TJEI_LUMA_DC] = tjei_default_ht_luma_dc_len; + state->ht_bits[TJEI_LUMA_AC] = tjei_default_ht_luma_ac_len; + state->ht_bits[TJEI_CHROMA_DC] = tjei_default_ht_chroma_dc_len; + state->ht_bits[TJEI_CHROMA_AC] = tjei_default_ht_chroma_ac_len; + + state->ht_vals[TJEI_LUMA_DC] = tjei_default_ht_luma_dc; + state->ht_vals[TJEI_LUMA_AC] = tjei_default_ht_luma_ac; + state->ht_vals[TJEI_CHROMA_DC] = tjei_default_ht_chroma_dc; + state->ht_vals[TJEI_CHROMA_AC] = tjei_default_ht_chroma_ac; + + // How many codes in total for each of LUMA_(DC|AC) and CHROMA_(DC|AC) + int32_t spec_tables_len[4] = { 0 }; + + int i, k; + + for ( i = 0; i < 4; ++i ) { + for ( k = 0; k < 16; ++k ) { + spec_tables_len[i] += state->ht_bits[i][k]; + } + } + + // Fill out the extended tables.. + uint8_t huffsize[4][257]; + uint16_t huffcode[4][256]; + for ( i = 0; i < 4; ++i ) { + assert (256 >= spec_tables_len[i]); + tjei_huff_get_code_lengths(huffsize[i], state->ht_bits[i]); + tjei_huff_get_codes(huffcode[i], huffsize[i], spec_tables_len[i]); + } + for ( i = 0; i < 4; ++i ) { + int64_t count = spec_tables_len[i]; + tjei_huff_get_extended(state->ehuffsize[i], + state->ehuffcode[i], + state->ht_vals[i], + &huffsize[i][0], + &huffcode[i][0], count); + } +} + +static int tjei_encode_main(TJEState* state, + const unsigned char* src_data, + const int width, + const int height, + const int src_num_components) +{ + if (src_num_components != 3 && src_num_components != 4) { + return 0; + } + + if (width > 0xffff || height > 0xffff) { + return 0; + } + +#if TJE_USE_FAST_DCT + struct TJEProcessedQT pqt; + // Again, taken from classic japanese implementation. + // + /* For float AA&N IDCT method, divisors are equal to quantization + * coefficients scaled by scalefactor[row]*scalefactor[col], where + * scalefactor[0] = 1 + * scalefactor[k] = cos(k*PI/16) * sqrt(2) for k=1..7 + * We apply a further scale factor of 8. + * What's actually stored is 1/divisor so that the inner loop can + * use a multiplication rather than a division. + */ + static const float aan_scales[] = { + 1.0f, 1.387039845f, 1.306562965f, 1.175875602f, + 1.0f, 0.785694958f, 0.541196100f, 0.275899379f + }; + + int x, y; + + // build (de)quantization tables + for(y=0; y<8; y++) { + for(x=0; x<8; x++) { + int i = y*8 + x; + pqt.luma[y*8+x] = 1.0f / (8 * aan_scales[x] * aan_scales[y] * state->qt_luma[tjei_zig_zag[i]]); + pqt.chroma[y*8+x] = 1.0f / (8 * aan_scales[x] * aan_scales[y] * state->qt_chroma[tjei_zig_zag[i]]); + } + } +#endif + + { // Write header + TJEJPEGHeader header; + // JFIF header. + header.SOI = tjei_be_word(0xffd8); // Sequential DCT + header.APP0 = tjei_be_word(0xffe0); + + uint16_t jfif_len = sizeof(TJEJPEGHeader) - 4 /*SOI & APP0 markers*/; + header.jfif_len = tjei_be_word(jfif_len); + memcpy(header.jfif_id, (void*)tjeik_jfif_id, 5); + header.version = tjei_be_word(0x0102); + header.units = 0x01; // Dots-per-inch + header.x_density = tjei_be_word(0x0060); // 96 DPI + header.y_density = tjei_be_word(0x0060); // 96 DPI + header.x_thumb = 0; + header.y_thumb = 0; + tjei_write(state, &header, sizeof(TJEJPEGHeader), 1); + } + { // Write comment + TJEJPEGComment com; + uint16_t com_len = 2 + sizeof(tjeik_com_str) - 1; + // Comment + com.com = tjei_be_word(0xfffe); + com.com_len = tjei_be_word(com_len); + memcpy(com.com_str, (void*)tjeik_com_str, sizeof(tjeik_com_str)-1); + tjei_write(state, &com, sizeof(TJEJPEGComment), 1); + } + + // Write quantization tables. + tjei_write_DQT(state, state->qt_luma, 0x00); + tjei_write_DQT(state, state->qt_chroma, 0x01); + + { // Write the frame marker. + TJEFrameHeader header; + header.SOF = tjei_be_word(0xffc0); + header.len = tjei_be_word(8 + 3 * 3); + header.precision = 8; + assert(width <= 0xffff); + assert(height <= 0xffff); + header.width = tjei_be_word((uint16_t)width); + header.height = tjei_be_word((uint16_t)height); + header.num_components = 3; + uint8_t tables[3] = { + 0, // Luma component gets luma table (see tjei_write_DQT call above.) + 1, // Chroma component gets chroma table + 1, // Chroma component gets chroma table + }; + + int i; + + for (i = 0; i < 3; ++i) { + TJEComponentSpec spec; + spec.component_id = (uint8_t)(i + 1); // No particular reason. Just 1, 2, 3. + spec.sampling_factors = (uint8_t)0x11; + spec.qt = tables[i]; + + header.component_spec[i] = spec; + } + // Write to file. + tjei_write(state, &header, sizeof(TJEFrameHeader), 1); + } + + tjei_write_DHT(state, state->ht_bits[TJEI_LUMA_DC], state->ht_vals[TJEI_LUMA_DC], TJEI_DC, 0); + tjei_write_DHT(state, state->ht_bits[TJEI_LUMA_AC], state->ht_vals[TJEI_LUMA_AC], TJEI_AC, 0); + tjei_write_DHT(state, state->ht_bits[TJEI_CHROMA_DC], state->ht_vals[TJEI_CHROMA_DC], TJEI_DC, 1); + tjei_write_DHT(state, state->ht_bits[TJEI_CHROMA_AC], state->ht_vals[TJEI_CHROMA_AC], TJEI_AC, 1); + + // Write start of scan + { + TJEScanHeader header; + header.SOS = tjei_be_word(0xffda); + header.len = tjei_be_word((uint16_t)(6 + (sizeof(TJEFrameComponentSpec) * 3))); + header.num_components = 3; + + uint8_t tables[3] = { + 0x00, + 0x11, + 0x11, + }; + + int i; + + for (i = 0; i < 3; ++i) { + TJEFrameComponentSpec cs; + // Must be equal to component_id from frame header above. + cs.component_id = (uint8_t)(i + 1); + cs.dc_ac = (uint8_t)tables[i]; + + header.component_spec[i] = cs; + } + header.first = 0; + header.last = 63; + header.ah_al = 0; + tjei_write(state, &header, sizeof(TJEScanHeader), 1); + + } + // Write compressed data. + + float du_y[64]; + float du_b[64]; + float du_r[64]; + + // Set diff to 0. + int pred_y = 0; + int pred_b = 0; + int pred_r = 0; + + // Bit stack + uint32_t bitbuffer = 0; + uint32_t location = 0; + + int off_x, off_y; + + for ( y = 0; y < height; y += 8 ) { + for ( x = 0; x < width; x += 8 ) { + // Block loop: ==== + for ( off_y = 0; off_y < 8; ++off_y ) { + for ( off_x = 0; off_x < 8; ++off_x ) { + int block_index = (off_y * 8 + off_x); + + int src_index = (((y + off_y) * width) + (x + off_x)) * src_num_components; + + int col = x + off_x; + int row = y + off_y; + + if(row >= height) { + src_index -= (width * (row - height + 1)) * src_num_components; + } + if(col >= width) { + src_index -= (col - width + 1) * src_num_components; + } + assert(src_index < width * height * src_num_components); + + uint8_t r = src_data[src_index + 0]; + uint8_t g = src_data[src_index + 1]; + uint8_t b = src_data[src_index + 2]; + + float luma = 0.299f * r + 0.587f * g + 0.114f * b - 128; + float cb = -0.1687f * r - 0.3313f * g + 0.5f * b; + float cr = 0.5f * r - 0.4187f * g - 0.0813f * b; + + du_y[block_index] = luma; + du_b[block_index] = cb; + du_r[block_index] = cr; + } + } + + tjei_encode_and_write_MCU(state, du_y, +#if TJE_USE_FAST_DCT + pqt.luma, +#else + state->qt_luma, +#endif + state->ehuffsize[TJEI_LUMA_DC], state->ehuffcode[TJEI_LUMA_DC], + state->ehuffsize[TJEI_LUMA_AC], state->ehuffcode[TJEI_LUMA_AC], + &pred_y, &bitbuffer, &location); + tjei_encode_and_write_MCU(state, du_b, +#if TJE_USE_FAST_DCT + pqt.chroma, +#else + state->qt_chroma, +#endif + state->ehuffsize[TJEI_CHROMA_DC], state->ehuffcode[TJEI_CHROMA_DC], + state->ehuffsize[TJEI_CHROMA_AC], state->ehuffcode[TJEI_CHROMA_AC], + &pred_b, &bitbuffer, &location); + tjei_encode_and_write_MCU(state, du_r, +#if TJE_USE_FAST_DCT + pqt.chroma, +#else + state->qt_chroma, +#endif + state->ehuffsize[TJEI_CHROMA_DC], state->ehuffcode[TJEI_CHROMA_DC], + state->ehuffsize[TJEI_CHROMA_AC], state->ehuffcode[TJEI_CHROMA_AC], + &pred_r, &bitbuffer, &location); + + + } + } + + // Finish the image. + { // Flush + if (location > 0 && location < 8) { + tjei_write_bits(state, &bitbuffer, &location, (uint16_t)(8 - location), 0); + } + } + uint16_t EOI = tjei_be_word(0xffd9); + tjei_write(state, &EOI, sizeof(uint16_t), 1); + + if (state->output_buffer_count) { + state->write_context.func(state->write_context.context, state->output_buffer, (int)state->output_buffer_count); + state->output_buffer_count = 0; + } + + return 1; +} + +int tje_encode_to_file(const char* dest_path, + const int width, + const int height, + const int num_components, + const unsigned char* src_data) +{ + int res = tje_encode_to_file_at_quality(dest_path, 2, width, height, num_components, src_data); + return res; +} + +static void tjei_stdlib_func(void* context, void* data, int size) +{ + FILE* fd = (FILE*)context; + fwrite(data, size, 1, fd); +} + +// Define public interface. +int tje_encode_to_file_at_quality(const char* dest_path, + const int quality, + const int width, + const int height, + const int num_components, + const unsigned char* src_data) +{ + FILE* fd = fopen(dest_path, "wb"); + if (!fd) { + tje_log("Could not open file for writing."); + return 0; + } + + int result = tje_encode_with_func(tjei_stdlib_func, fd, + quality, width, height, num_components, src_data); + + result |= 0 == fclose(fd); + + return result; +} + +int tje_encode_with_func(tje_write_func* func, + void* context, + const int quality, + const int width, + const int height, + const int num_components, + const unsigned char* src_data) +{ + if (quality < 1 || quality > 3) { + tje_log("[ERROR] -- Valid 'quality' values are 1 (lowest), 2, or 3 (highest)\n"); + return 0; + } + + TJEState state = { 0 }; + int i; + + uint8_t qt_factor = 1; + switch(quality) { + case 3: + for ( i = 0; i < 64; ++i ) { + state.qt_luma[i] = 1; + state.qt_chroma[i] = 1; + } + break; + case 2: + qt_factor = 10; + // don't break. fall through. + case 1: + for ( i = 0; i < 64; ++i ) { + state.qt_luma[i] = tjei_default_qt_luma_from_spec[i] / qt_factor; + if (state.qt_luma[i] == 0) { + state.qt_luma[i] = 1; + } + state.qt_chroma[i] = tjei_default_qt_chroma_from_paper[i] / qt_factor; + if (state.qt_chroma[i] == 0) { + state.qt_chroma[i] = 1; + } + } + break; + default: + assert(!"invalid code path"); + break; + } + + TJEWriteContext wc = { 0 }; + + wc.context = context; + wc.func = func; + + state.write_context = wc; + + + tjei_huff_expand(&state); + + int result = tjei_encode_main(&state, src_data, width, height, num_components); + + return result; +} +// ============================================================ +#endif // TJE_IMPLEMENTATION +// ============================================================ +// +#if defined(__GNUC__) || defined(__clang__) +#pragma GCC diagnostic pop +#endif + + +#ifdef __cplusplus +} // extern C +#endif + diff --git a/source/lib/vc5_common/CMakeLists.txt b/source/lib/vc5_common/CMakeLists.txt new file mode 100644 index 0000000..090b212 --- /dev/null +++ b/source/lib/vc5_common/CMakeLists.txt @@ -0,0 +1,20 @@ +# library +set( LIB_NAME vc5_common ) + +# get source files +file( GLOB SRC_FILES "*.c" ) + +# get include files +file( GLOB INC_FILES "*.h" ) + +# add include files from other folders +include_directories( "../common/private" ) +include_directories( "../common/public" ) + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/source/lib/vc5_common/bitstream.c b/source/lib/vc5_common/bitstream.c new file mode 100755 index 0000000..cef4312 --- /dev/null +++ b/source/lib/vc5_common/bitstream.c @@ -0,0 +1,445 @@ +/*! @file bitstream.c + * + * @brief Implementation of a bitstream for reading bits from a byte stream. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +/*! + @brief Return a mask with the specified number of bits set to one + */ +BITWORD BitMask(int n) +{ + if (n < bit_word_count) + { + BITWORD mask = 0; + if (n > 0) { + mask = ((1 << n) - 1); + } + return mask; + } + return BIT_WORD_MAX; +} + +/*! + @brief Initialize a bitstream data structure + + This routine is the constructor for the bitstream data type. + + The sample offset stack is used to mark the offset to a position + in the bitstream for computing the size field of sample chunks. + */ +CODEC_ERROR InitBitstream(BITSTREAM *bitstream) +{ + if (bitstream != NULL) + { + bitstream->error = BITSTREAM_ERROR_OKAY; + bitstream->stream = NULL; + bitstream->buffer = 0; + bitstream->count = 0; + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + // Initialize the stack of sample offsets + memset(bitstream->sample_offset_stack, 0, sizeof(bitstream->sample_offset_stack)); + bitstream->sample_offset_count = 0; +#endif + + return CODEC_ERROR_OKAY; + } + + return CODEC_ERROR_NULLPTR; +} + +/*! + @brief Attach a bitstream to a byte stream. + + It is permitted for the byte stream to be NULL, in which case the + bitstream will not be able to replenish its internal buffer, but + the consequences are undefined. + */ +CODEC_ERROR AttachBitstream(struct _bitstream *bitstream, struct _stream *stream) +{ + assert(bitstream != NULL); + bitstream->stream = stream; + return CODEC_ERROR_OKAY; +} + +/*! + @brief Detach a bitstream from a byte stream. + + Any resources allocated by the bitstream are released without deallocating + the bitstream data structure itself. The byte stream associated with the + bitstream is not closed by this routine. The byte stream must be closed, + if and when appropriate, by the caller. + */ +CODEC_ERROR ReleaseBitstream(BITSTREAM *bitstream) +{ + //TODO: What cleanup needs to be performed? + (void) bitstream; + return CODEC_ERROR_OKAY; +} + +/*! + @brief Return the specified number of bits from the bitstream + */ +BITWORD GetBits(BITSTREAM *stream, BITCOUNT count) +{ + // Return zero if the request cannot be satisfied + BITWORD bits = 0; + + // Check that the number of requested bits is valid + //assert(0 <= count && count <= bit_word_count); + assert(count <= bit_word_count); + + // Check that the unused portion of the bit buffer is empty + assert((stream->buffer & BitMask(bit_word_count - stream->count)) == 0); + + if (count == 0) goto finish; + + // Are there enough bits in the buffer to satisfy the request? + if (count <= stream->count) + { + // Right align the requested number of bits in the bit buffer + bits = (stream->buffer >> (bit_word_count - count)); + + // Reduce the number of bits in the bit buffer + stream->buffer <<= count; + stream->count = (stream->count - count); + } + else + { + BITCOUNT low_bit_count; + + // Use the remaining bits in the bit buffer + assert(stream->count > 0 || stream->buffer == 0); + bits = (stream->buffer >> (bit_word_count - count)); + + // Compute the number of bits to be used from the next word + low_bit_count = count - stream->count; + stream->count = 0; + assert(low_bit_count > 0); + + // Fill the bit buffer + assert(stream->count == 0); + GetBuffer(stream); + assert(stream->count >= low_bit_count); + + // Use the new bits in the bit buffer + bits |= (stream->buffer >> (bit_word_count - low_bit_count)); + + // Reduce the number of bits in the bit buffer + if (low_bit_count < bit_word_count) { + stream->buffer <<= low_bit_count; + } + else { + stream->buffer = 0; + } + assert(low_bit_count <= stream->count); + stream->count = (stream->count - low_bit_count); + } + +finish: + // The bit count should never be negative or larger than the size of the bit buffer + //assert(0 <= stream->count && stream->count <= bit_word_count); + assert(stream->count <= bit_word_count); + + // The unused bits in the bit buffer should all be zero + assert((stream->buffer & BitMask(bit_word_count - stream->count)) == 0); + + // The unused bits in the result should all be zero + assert((bits & ~BitMask(count)) == 0); + + return bits; +} + +/*! + @brief Read more bits and append to an existing word of bits + + Read the specified number of bits from the bitstream and append + the new bits to the right end of the word supplied as an argument. + This is a convenience routine that is used to accumulate bits that + may match a codeword. + */ +BITWORD AddBits(BITSTREAM *bitstream, BITWORD bits, BITCOUNT count) +{ + BITWORD new_bits = GetBits(bitstream, count); + assert((new_bits & ~BitMask(count)) == 0); + + bits = (bits << count) | new_bits; + + return bits; +} + +/*! + @brief Write a longword (32 bits) to the stream + */ +CODEC_ERROR PutLong(BITSTREAM *stream, BITWORD longword) +{ + return PutBits(stream, longword, bit_word_count); +} + +/*! + @brief Write the specified number of bits to the bitstream + */ +CODEC_ERROR PutBits(BITSTREAM *stream, BITWORD bits, BITCOUNT count) +{ + BITCOUNT unused_bit_count; + + if (count == 0) { + return CODEC_ERROR_OKAY; + } + + // Check that the unused portion of the input bits is empty + assert((bits & (BitMask(bit_word_count - count) << count)) == 0); + + // Check that the number of input bits is valid + //assert(0 <= count && count <= bit_word_count); + assert(count <= bit_word_count); + + // Check that the unused portion of the bit buffer is empty + unused_bit_count = bit_word_count - stream->count; + assert((stream->buffer & BitMask(unused_bit_count)) == 0); + + // Is there room in the bit buffer for the new bits? + if (count <= unused_bit_count) + { + // Fill the remaining space in the bit buffer + stream->buffer |= (bits << (unused_bit_count - count)); + + // Reduce the number of unused bits in the bit buffer + stream->count += count; + } + else + { + // Any room in the bit buffer? + if (unused_bit_count > 0) + { + // Use the number of input bits that will fit in the bit buffer + stream->buffer |= (bits >> (count - unused_bit_count)); + + // Reduce the number of input bits by the amount used + count -= unused_bit_count; + //assert(count >= 0); + } + + // Write the bit buffer to the byte stream + PutWord(stream->stream, stream->buffer ); + + // Insert the remaining input bits into the bit buffer + stream->buffer = (bits << (bit_word_count - count)); + + // Increment the number of bits in the bit buffer + stream->count = count; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Fill the internal bitstream buffer by reading a byte stream + + @todo Need to modify this routine to return an error code if it cannot + read from the byte stream associated with the bitstream. + */ +CODEC_ERROR GetBuffer(BITSTREAM *bitstream) +{ + // Need to signal an underflow error? + if (! (bitstream != NULL && bitstream->stream != NULL)) { + assert(0); + + if( bitstream->error == BITSTREAM_ERROR_UNDERFLOW ) + return CODEC_ERROR_OUTOFMEMORY; + } + + // The bit buffer should be empty + assert(bitstream->count == 0); + + // Fill the bit buffer with a word from the byte stream + bitstream->buffer = Swap32(GetWord(bitstream->stream)); + bitstream->count = bit_word_count; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write the internal bitstream buffer to a byte stream + + @todo Need to modify this routine to return an error code if it cannot + write to the byte stream associated with the bitstream. + */ +CODEC_ERROR PutBuffer(BITSTREAM *bitstream) +{ + //TODO: Need to signal an overflow error + assert(bitstream != NULL && bitstream->stream != NULL); + + // The bit buffer should be full + assert(bitstream->count == bit_word_count); + + // Write the bit buffer to the byte stream + PutWord(bitstream->stream, bitstream->buffer ); + + // Empty the bit buffer + bitstream->buffer = 0; + bitstream->count = 0; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Convert a bitstream error code to a codec error codec + + The bitstream and byte stream modules use a separate enumeration + for error codes since these modules are used in other applications. + The bistream error code is embedded in a range of codec error codes + that are reserved for bitstream errors. + */ +CODEC_ERROR CodecErrorBitstream(BITSTREAM_ERROR error) +{ + uint32_t codec_error = CODEC_ERROR_BITSTREAM; + codec_error |= (uint32_t)error; + return (CODEC_ERROR)codec_error; +} + +/*! + @brief Convert a bitstream error code into a codec error code + + The bitstream and byte stream modules might be used in other + applications and have their own errors codes. This routine + embeds a bitstream error code into a codec error code. The + bitstream error code is carried in the low bits of the codec + error code. + + @todo Eliminate separate error codes for bitstreams? + */ +CODEC_ERROR BitstreamCodecError(BITSTREAM *bitstream) +{ + assert(bitstream != NULL); + if (! (bitstream != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + return CodecErrorBitstream(bitstream->error); +} + +/*! + @brief Read a block of bytes from the bitstream + */ +CODEC_ERROR GetByteArray(BITSTREAM *bitstream, uint8_t *array, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) + { + array[i] = GetBits(bitstream, 8); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write a block of bytes into the bitstream + */ +CODEC_ERROR PutByteArray(BITSTREAM *bitstream, const uint8_t *array, size_t size) +{ + size_t i; + + for (i = 0; i < size; i++) + { + PutBits(bitstream, array[i], 8); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write any bits in the buffer to the byte stream + */ +CODEC_ERROR FlushBitstream(BITSTREAM *bitstream) +{ + // Any bits remaining in the bit buffer? + if (bitstream->count > 0) + { + // Write the bit buffer to the output stream + PutBuffer(bitstream); + } + + // Indicate that the bitstream buffer is empty + bitstream->count = 0; + bitstream->buffer = 0; + + // Flush the output stream + FlushStream(bitstream->stream); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Get the current position in the byte stream + + The current position in the byte stream associated with the + bitstream is returned. The intent is to allow the bitstream + (and the associated byte stream) to be restored to the saved + position. + + @todo Need to record the state of the bitstream buffer and + bit count so that the entire bitstream state can be restored. + */ +size_t GetBitstreamPosition(BITSTREAM *bitstream) +{ + if (bitstream->count == bit_word_count) { + PutBuffer(bitstream); + } + + // The bit buffer must be empty + assert(bitstream->count == 0); + + // The bit buffer must be empty + return (bitstream->stream->byte_count); +} + +/*! + @brief Rewind the bitstream + + This routine rewinds the bitstream to the beginning of the byte stream + that has been attached to the bitstream. The byte stream is also reset. + + If the byte stream could not be reset, then the internal bitstream state + is not reset. + */ +CODEC_ERROR RewindBitstream(BITSTREAM *bitstream) +{ + assert(bitstream != NULL); + if (! (bitstream != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + if (bitstream->stream != NULL) { + CODEC_ERROR error = RewindStream(bitstream->stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + } + + // Reset the bitstream internal state + bitstream->buffer = 0; + bitstream->count = 0; + bitstream->error = BITSTREAM_ERROR_OKAY; + + return CODEC_ERROR_OKAY; +} + diff --git a/source/lib/vc5_common/bitstream.h b/source/lib/vc5_common/bitstream.h new file mode 100755 index 0000000..85c2a25 --- /dev/null +++ b/source/lib/vc5_common/bitstream.h @@ -0,0 +1,135 @@ +/*! @file bitstream.h + * + * @brief Declaration of the bitstream data structure. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef BITSTREAM_H +#define BITSTREAM_H + +/*! + @brief Bitstream error codes + + The bitstream contains its own enumeration of error codes since this + module may be used in other applications. + */ +typedef enum _bitstream_error +{ + BITSTREAM_ERROR_OKAY = 0, //!< No error + BITSTREAM_ERROR_UNDERFLOW, //!< No unread bits remaining in the bitstream + BITSTREAM_ERROR_OVERFLOW, //!< No more bits can be written to the bitstream + BITSTREAM_ERROR_BADTAG, //!< Unexpected tag found in the bitstream + + //TODO: Add more bitstream errors + +} BITSTREAM_ERROR; + +typedef uint32_t BITWORD; //!< Data type of the internal bitstream buffer + +typedef uint_fast8_t BITCOUNT; //!< Number of bits in the bitsteam buffer + +//! Maximum number of bits in a bit word +static const BITCOUNT bit_word_count = 32; + +//! Maximum value of a bit word +#define BIT_WORD_MAX 0xFFFFFFFF + +//! Sample offset stack depth +#define MAX_SAMPLE_OFFSET_COUNT 8 + +/*! + @brief Declaration of the bitstream data structure + + The bitstream uses a byte stream to read bytes from a file or a buffer + in memory. This isolates that bitstream module from the type of byte + stream. + */ +typedef struct _bitstream +{ + BITSTREAM_ERROR error; //!< Error while processing the bitstream + struct _stream *stream; //!< Stream for reading bytes into the buffer + BITWORD buffer; //!< Internal buffer holds remaining bits + BITCOUNT count; //!< Number of bits remaining in the buffer + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + /*! + The sample offset stack is used to record offsets to the start of nested + syntax structures. For example, a sample size segment is written into the + bitstream with a size of zero, since the size of the syntax element is not + known in advance. The offset to the sample size segment is pushed onto the + sample offset stack so that the location of the sample size segment can be + updated with the actual size of a syntax element after the complete element + is written into the bitstream. + + This data structure is called the ChunkSizeOffset in the current codec + implementation. + */ + uint32_t sample_offset_stack[MAX_SAMPLE_OFFSET_COUNT]; + + //! Number of entries in the sample offset stack + uint_fast8_t sample_offset_count; +#endif + +} BITSTREAM; + +#ifdef __cplusplus +extern "C" { +#endif + + BITWORD BitMask(int n); + + CODEC_ERROR CodecErrorBitstream(BITSTREAM_ERROR error); + + // Initialize a bitstream data structure + CODEC_ERROR InitBitstream(BITSTREAM *bitstream); + + // Bind the bitstream to a byte stream + CODEC_ERROR AttachBitstream(struct _bitstream *bitstream, struct _stream *stream); + + CODEC_ERROR ReleaseBitstream(BITSTREAM *stream); + + BITWORD GetBits(BITSTREAM *stream, BITCOUNT count); + + CODEC_ERROR PutBits(BITSTREAM *stream, BITWORD bits, BITCOUNT count); + + BITWORD AddBits(BITSTREAM *stream, BITWORD bits, BITCOUNT count); + + CODEC_ERROR GetBuffer(BITSTREAM *stream); + + CODEC_ERROR PutBuffer(BITSTREAM *stream); + + CODEC_ERROR PutLong(BITSTREAM *stream, BITWORD longword); + + // Rewind the bitstream and the associated byte stream + CODEC_ERROR RewindBitstream(BITSTREAM *bitstream); + + CODEC_ERROR BitstreamCodecError(BITSTREAM *bitstream); + + // Return the current position of the bitstream pointer in the sample + size_t GetBitstreamPosition(BITSTREAM *stream); + + CODEC_ERROR FlushBitstream(BITSTREAM *bitstream); + + CODEC_ERROR GetByteArray(BITSTREAM *bitstream, uint8_t *array, size_t size); + + CODEC_ERROR PutByteArray(BITSTREAM *bitstream, const uint8_t *block, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif // BITSTREAM_H diff --git a/source/lib/vc5_common/codec.c b/source/lib/vc5_common/codec.c new file mode 100755 index 0000000..2308f75 --- /dev/null +++ b/source/lib/vc5_common/codec.c @@ -0,0 +1,201 @@ +/*! @file codec.c + * + * @brief Implementation of functions that are common to the + * reference decoder and encoder + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +/*! + @brief Initialize the codec state to before encoding or decoding the bitstream + + Most of the codec state can be deduced from the decoding parameters. + For example, the dimensions of the first wavelet band in the bitstream + can be deduced from the encoded frame dimensions and the structure of + the wavelet tree. + + The encoder will not insert parameters into the bitstream if the values + of the parameters are the same as in the codec state. This routine + should initialize the codec state with correct values if those values + can be inferred by the decoder, otherwise the use incorrect or default + values. + + Note that the default encoded format is YUV 4:2:2, but this format is not + supported by the baseline profile encoder so the encoded format must be + explicitly written into the bitstream. + + @todo Add more default values required to properly initialize the codec state. +*/ +CODEC_ERROR PrepareCodecState(CODEC_STATE *codec) +{ + // Initialize the channel to channel number zero + codec->channel_number = 0; + + // Initialize the subband number to subband zero (the lowpass band) + codec->subband_number = 0; + + // The number of subbands per channel is a constant + codec->subband_count = 10; + + // The default precision of pixels in the input frame is ten bits + codec->bits_per_component = 12; + + // Force the encoder to insert the encoded dimensions into the bitstream + //codec->encoded.width = 0; + //codec->encoded.height = 0; + + /* + TODO: Do not have to encode the display dimensions into the bitstream if the decoder + can infer the display dimensions from the encoded dimensions and format, but must + encode the display dimensions if the encoded dimensions include padding. + + TODO: Check that this matches the current decoder implementation. + */ + + // Set the default precision for encoding lowpass band coefficients + codec->lowpass_precision = 16; + + //TODO: Set more default values in the codec state + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Reformat a segment value into the encoder version + + The version of the encoder that created the clip may be encoded into every sample. + + @todo Document the encoder version number format +*/ +uint32_t EncoderVersion(uint32_t value) +{ + return (((value >> 12) & 0x0F) << 16) | + (((value >> 8) & 0x0F) << 8) | + ((value) & 0xFF); +} + +/*! + @brief Unpack the version tag value into its components +*/ +void SetCodecVersion(uint8_t version[3], uint16_t value) +{ + version[0] = (uint8_t)((value >> 12) & 0x0F); // Major version + version[1] = (uint8_t)((value >> 8) & 0x0F); // Minor version + version[2] = (uint8_t)(value & 0xFF); // Revision +} + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) +/*! + @brief Return true if the image format is valid +*/ +bool ValidImageFormat(IMAGE_FORMAT image_format) +{ + if (0 < image_format && image_format < IMAGE_FORMAT_COUNT) { + return true; + } + return false; +} +#endif + +/*! + @brief Unpack the tag value into the prescale table + + The prescale table contains the prescale value for each wavelet in the + transform. The prescale value is a right shift that is applied to the + input data before the wavelet is computed. + + The prescale table is used for all transforms and does not depend on the + channel number. +*/ +CODEC_ERROR UpdatePrescaleTable(CODEC_STATE *codec, TAGWORD value) +{ + int wavelet_index; + + for (wavelet_index = 0; wavelet_index < MAX_WAVELET_COUNT; wavelet_index++) + { + // Unpack the prescale value + int prescale_value = (value >> (14 - wavelet_index * 2)) & 0x03; + assert(0 <= prescale_value && prescale_value < UINT8_MAX); + + // Store the prescale value in the codec state + codec->prescale_table[wavelet_index] = (uint_fast8_t)prescale_value; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Update the flags that describe the frame structure + + The frame structure includes characteristics such as interlaced versus + progressive and top or bottom field first. +*/ +CODEC_ERROR UpdateFrameStructureFlags(CODEC_STATE *codec, TAGWORD value) +{ + codec->progressive = !(value & IMAGE_STRUCTURE_INTERLACED); + codec->top_field_first = !(value & IMAGE_STRUCTURE_BOTTOM_FIELD_FIRST); + codec->frame_inverted = (value & IMAGE_STRUCTURE_BOTTOM_ROW_FIRST); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Initialize the codec state using the default constructor + + This routine is like a default constructor in C++ as it guarantees that + the codec state is initialized to a know starting state with all pointers + set to NULL and all counters set to zero. + + The routine @ref PrepareCodecState is used to set default values for the + codec state prior to decoding a sample. +*/ +CODEC_ERROR InitCodecState(CODEC_STATE *state) +{ + // Clear the codec state + memset(state, 0, sizeof(CODEC_STATE)); + return CODEC_ERROR_OKAY; +} + +/*! + @brief Set the flags that determine the band coding + + There can be up to 15 different codebooks as specified by the lower + four bigs in the band coding flags. Use the default codebook if the + active codebook is zero. + + The baseline profile does not allow difference coding or alternative + codebooks. +*/ +CODEC_ERROR SetBandCoding(CODEC_STATE *codec, TAGWORD value) +{ + (void) codec; + return CODEC_ERROR_OKAY; +} + +/*! + @brief Return true if the specified part is enabled at runtime + + This predicate is used to test whether a specific part in the VC-5 standard is + enabled at runtime by this codec implementation. + +*/ +bool IsPartEnabled(ENABLED_PARTS enabled_parts, int part_number) +{ + return ((enabled_parts & VC5_PART_MASK(part_number)) != 0); +} + diff --git a/source/lib/vc5_common/codec.h b/source/lib/vc5_common/codec.h new file mode 100755 index 0000000..6763091 --- /dev/null +++ b/source/lib/vc5_common/codec.h @@ -0,0 +1,315 @@ +/*! @file codec.h + * + * @brief State of the decoder while decoding a sample. The codec state + * contains information about the current state of the decoding process. + * The codec state is updated as the bitstream is decoded. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODEC_H +#define CODEC_H + +#include "syntax.h" + +static const SEGMENT StartMarkerSegment = ((0x56 << 24) | (0x43 << 16) | (0x2D << 8) | 0x35); + +/*! + @brief Tags that define elements in the bitstream + + All syntax elements in the encoded bitstream begin with a 16-bit tag that + specifies the type of element. The 16-bit tag is followed by a 16-bit value, + forming a tag-value pair. + + If the tag is a negative number, then the actual tag is the negation of the + tag value and the negative sign indicates that the tag and its value are an + optional tag value pair. If the tag is a positive value, then the segment + is required. A decoder must be able to decode all required tag-value pairs, + but can skip tag-value pairs that are optional. + + In a VC-5 Part 1 bitstream, the image width and height are an upper bound on the + dimensions of each channel represented in the bitstream. In a VC-5 Part 3 bitstream, + the image width and height are the actual dimensions of the image represented in the + bitstream. The width and height of the image and each pattern element is sufficient + to determine the width and height of each component array. + + A range of tags is reserved for chunks and the value is the size of the chunk. +*/ +typedef enum _codec_tag +{ + CODEC_TAG_ImageWidth = 20, //!< Upper bound on the width of the image + CODEC_TAG_ImageHeight = 21, //!< Upper bound on the height of the image + CODEC_TAG_BitsPerComponent = 101, //!< Number of bits in the source image + CODEC_TAG_ChannelCount = 12, //!< Number of channels in the transform + CODEC_TAG_SubbandCount = 14, //!< Number of encoded subbands + CODEC_TAG_ChannelNumber = 62, //!< Channel number + CODEC_TAG_SubbandNumber = 48, //!< Subband number of this wavelet band + CODEC_TAG_LowpassPrecision = 35, //!< Number of bits per lowpass coefficient + CODEC_TAG_Quantization = 53, //!< Quantization applied to band + CODEC_TAG_PrescaleShift = 109, //!< Packed prescale shift for each wavelet level + CODEC_TAG_ChannelWidth = 104, //!< Width of the next channel in the bitstream + CODEC_TAG_ChannelHeight = 105, //!< Height of the next channel in the bitstream + + CODEC_TAG_LargeCodeblock = 0x6000, //!< Large chunk that contains a codeblock + + CODEC_TAG_SMALL_CHUNK = 0x4000, //!< Small chunk with a 16-bit payload size (in segments) + CODEC_TAG_LARGE_CHUNK = 0x2000, //!< Large chunk with a 24-bit payload size (in segments) + + //! Mask for detecting the tag for a small or large chunk (including codeblocks) + CODEC_TAG_CHUNK_MASK = (CODEC_TAG_SMALL_CHUNK | CODEC_TAG_LARGE_CHUNK), + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + // Codec tags used by VC-5 Part 3 bitstreams + CODEC_TAG_PatternWidth = 106, //!< Number of samples per row in each pattern element + CODEC_TAG_PatternHeight = 107, //!< Number of rows of samples in each pattern element + CODEC_TAG_ComponentsPerSample = 108, //!< Number of components in each sample in the pattern element + CODEC_TAG_ImageFormat = 84, //!< Format of the image represented by the bitstream + CODEC_TAG_MaxBitsPerComponent = 102, //!< Upper bound on the number of bits per component + + // Small chunk elements defined by VC-5 Part 3 + CODEC_TAG_VendorSpecificData = 0x4000, //!< Small chunk containing vendor-specific data + CODEC_TAG_InversePermutation = 0x4001, //!< Small chunk containing the inverse component permutation + CODEC_TAG_InverseTransform = 0x4002, //!< Small chunk containing the inverse component transform (8 bit representation) + CODEC_TAG_InverseTransform16 = 0x4003, //!< Small chunk containing the inverse component transform (16 bit representation) + CODEC_TAG_UniqueImageIdentifier = 0x4004, //!< Small chunk containing the identifier and sequence number for the image +#endif + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + CODEC_TAG_LayerCount = 120, //!< Number of layers in the bitstream + CODEC_TAG_LayerNumber = 121, //!< Number of the next layer in the bitstream + CODEC_TAG_LayerPattern = 122, //!< Mask indicating the use cases in the bitstream +#endif + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + CODEC_TAG_ImageCount = 130, //!< Number of image bitstream sections in the bitstream + CODEC_TAG_ImageNumber = 131, //!< Unique number assigned to an image bitstream section + + // Predefined codec tags for structures in the VC-5 bitstream + CODEC_TAG_ImageSectionTag = 0x2700, //!< Section that contains a single image + CODEC_TAG_HeaderSectionTag = 0x2500, //!< Section that contains the bitstream header + CODEC_TAG_LayerSectionTag = 0x2600, //!< Section that contains a single layer + CODEC_TAG_ChannelSectionTag = 0x2400, //!< Section that contains a single channel + CODEC_TAG_WaveletSectionTag = 0x2100, //!< Section that contains all subbands for one wavelet + CODEC_TAG_SubbandSectionTag = 0x2000, //!< Section that contains a single subband +#endif + +#if VC5_ENABLED_PART(VC5_PART_METADATA) + // Small and large chunks of metadata + CODEC_TAG_SmallMetadata = 0x4010, //!< Small chunk containing metadata tuples (VC-5 Part 7) + CODEC_TAG_LargeMetadata = 0x6100, //!< Large chunk containing metadata tuples (VC-5 Part 7) +#endif + +} CODEC_TAG; + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) +/*! + @brief Format of the encoded sample + + The VC-5 Part 3 can support four bitstream representations of the encoded image. + + The image format must be specified for any VC-5 Part 3 bitstream. +*/ +typedef enum _image_format +{ + IMAGE_FORMAT_UNKNOWN = 0, //!< The image format has not been specified + IMAGE_FORMAT_RAW = 4, //!< RAW image format (special case of CFA) + + /***** Add new encoded formats above this line *****/ + + IMAGE_FORMAT_COUNT, //!< Number of image formats that have been defined + +} IMAGE_FORMAT; + +#endif + + +/*! + @brief Band encoding method + + Several different schemes have been tried for entropy coding the highpass bands. + The baseline profile only supports the run lengths encoding method. + + The run lengths encoding method using a Huffman code to encode runs of zeros and + highpass coefficient magnitudes (unsigned). Runs of zeros can extend across row + boundaries, so large sections of a highpass band that are mostly zeros can be + encoded very efficiently. + + @todo Need to cull this list as many band encoding methods are no longer supported. +*/ +enum band_encoding { + BAND_ENCODING_ZEROTREE = 1, + BAND_ENCODING_CODEBOOK, + BAND_ENCODING_RUNLENGTHS, + BAND_ENCODING_16BIT, + BAND_ENCODING_LOSSLESS +}; + +/*! + The codec state contains information about the decoding process obtained + as a sample is decoded. The information is transient and is only used + while decoding a sample. The decoder data structure contains information + that should persist from onen sample to the next. + + The codec state is initialized using information in the decoder data structure + at the start of decoding a sample. + + The intent is that the encoder can operate the same state machine during encoding + and any information available in the state machine does not have to be encoded into + the sample as it is assumed that the decoder can and will derive the same information. + For example, the dimensions of the first subband can be computed from the encoded + dimensions and the number of wavelet levels, so it is not necessary to encode this + information. Likewise, after the last band in a wavelet is decoded the dimensions + of the bands in the wavelet at the next level can be deduced and it is not necessary + to encode this information into the sample. +*/ +typedef struct _codec_state +{ + uint16_t channel_number; //!< Index of current channel being decoded + DIMENSION channel_width; //!< Width of the next channel in the bitstream + DIMENSION channel_height; //!< Height of the next channel in the bitstream + PRECISION bits_per_component; //!< Precision of the component array (in bits) + + uint16_t subband_number; //!< Index of current subband being decoded + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + IMAGE_FORMAT image_format; //!< Format of the image represented by the bitstream + DIMENSION pattern_width; //!< Width of the pattern elements (in samples) + DIMENSION pattern_height; //!< Height of the pattern elements (in rows) + DIMENSION components_per_sample; //!< Number of components in each sample in the pattern element + PRECISION max_bits_per_component; //!< Maximum number of bits for each value in the component arrays +#endif + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + uint_least8_t layer_count; +#endif + + uint_least8_t channel_count; //!< Number of channels in the current layer + uint_least8_t wavelet_count; //!< Number of wavelets in the current layer + uint_least8_t subband_count; //!< Number of suibbands in the current layer + + //! The channel position is used for skipping subbands and jumping to particular channels + size_t channel_position; + + uint32_t encoded_format; //!< Internal encoded representation + uint32_t encoded_quality; //!< Quality setting of the encoded video + + uint32_t decoded_subband_mask; //!< Indicates which subbands have been decoded + + bool progressive; //!< True if the encoded frame is progressive + + bool top_field_first; //!< True if the top field is encoded first + + bool frame_inverted; //!< True if the frame is encoded upside down + + uint_least8_t group_length; //!< Number of frames in a group of pictures (GOP) + + //! Indicates that enough of the sample has been read to allow decoding the sample + bool end_of_sample; + + //! Indicates that the layer has been decoded + bool end_of_layer; + + //! Most recent tag-value pair was a header parameter + bool header; + + //! Most recent syntax element was a codeblock (large chunk element) + bool codeblock; + + //! Parameters of the most recently decoded subband + struct + { + //DIMENSION width; //!< Width of the decoded band + //DIMENSION height; //!< Height of the decoded band + uint_least8_t subband; //!< Subband index + //uint_least8_t encoding; //!< Band encoding method + uint16_t quantization; //!< Quantization parameter + + } band; //!< Information about the current highpass band + + DIMENSION image_width; //!< Upper bound on the channel width + DIMENSION image_height; //!< Upper bound on the channel height + + PRECISION lowpass_precision; //!< Number of bits per lowpass coefficient + + /*! + @brief Table of prescale shifts applied before computing each wavelet transform + + The prescale shift was applied by the encoder to each input to the forward + wavelet transform. The table of prescale values is indexed by the same + index used for the wavelets in the transform. + */ + //uint_fast8_t prescale_table[MAX_WAVELET_COUNT]; + PRESCALE prescale_table[MAX_WAVELET_COUNT]; + + //! Picture aspect ratio read from the encoded sample + struct _picture_aspect_ratio + { + uint_least16_t x; //!< Relative width of the picture + uint_least16_t y; //!< Relative height of the picture + + } picture_aspect_ratio; //!< Picture aspect ratio read from the sample + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + uint32_t interlaced_flags; + uint32_t protection_flags; + + //!< Parameters of the current layer + struct + { + int width; //!< Width of the current layer + int height; //!< Height of the current layer + + } layer; //!< Information about the encoded layer from the sample + +#endif + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + int section_number; //!< Number of the most recent section encountered in the bitstream + int section_length; //!< Length of the most recent section element payload (in segments) +#endif + +} CODEC_STATE; + +#ifdef __cplusplus +extern "C" { +#endif + + // Initialize the codec state + CODEC_ERROR PrepareCodecState(CODEC_STATE *codec); + + uint32_t EncoderVersion(uint32_t value); + + uint32_t RepackedEncoderVersion(uint32_t value); + + void SetCodecVersion(uint8_t version[3], uint16_t value); + + CODEC_ERROR UpdatePrescaleTable(CODEC_STATE *codec, TAGWORD value); + + CODEC_ERROR UpdateSampleFlags(CODEC_STATE *codec, TAGWORD value); + + CODEC_ERROR UpdateCodecFlags(CODEC_STATE *codec, TAGWORD value); + + CODEC_ERROR UpdateFrameStructureFlags(CODEC_STATE *codec, TAGWORD value); + + CODEC_ERROR SetBandCoding(CODEC_STATE *codec, TAGWORD value); + + bool IsPartEnabled(ENABLED_PARTS enabled_parts, int part_number); + +#ifdef __cplusplus +} +#endif + +#endif // CODEC_H diff --git a/source/lib/vc5_common/codeset.h b/source/lib/vc5_common/codeset.h new file mode 100755 index 0000000..e220239 --- /dev/null +++ b/source/lib/vc5_common/codeset.h @@ -0,0 +1,44 @@ +/*! @file codeset.h + * + * @brief The codeset data structure includes the codebook and flags that + * indicate how to use the codebook. The codeset may also include tables + * that are derived from the codebook to facilite encoding and decoding. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODESET_H +#define CODESET_H + +/*! + @brief Codeset flags that determine how the codebook is used for encoding + + The codeset flags determine how the codebook in the codeset is used to + compute the tables for encoding coefficient magnitudes and runs of zeros. + + The companding curve is applied to the quantized coefficients before the + values are entropy coded to fit the coefficient magnitudes into the range + of magnitudes provided by the codebook. The companding curve is applied + when the encoding table for coefficient magnitudes is computed. +*/ +typedef enum _codeset_flags +{ + CODESET_FLAGS_COMPANDING_NONE = 0x0002, //!< Do not apply a companding curve + CODESET_FLAGS_COMPANDING_CUBIC = 0x0004, //!< Apply a cubic companding curve + +} CODESET_FLAGS; + +#endif // CODESET_H diff --git a/source/lib/vc5_common/common.h b/source/lib/vc5_common/common.h new file mode 100755 index 0000000..c0ff501 --- /dev/null +++ b/source/lib/vc5_common/common.h @@ -0,0 +1,53 @@ +/*! @file common.h + * + * @brief This file includes all of the header files that are used by + * the encoder and decoder. Including a single header file in all + * reference encoder and decoder source files ensures that all + * modules see the same header files in the same order. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMMON_H +#define COMMON_H + +#include "gpr_platform.h" +#include "gpr_allocator.h" +#include "gpr_buffer.h" +#include "gpr_rgb_buffer.h" + +#include "stdc_includes.h" + +#include "log.h" +#include "types.h" +#include "timer.h" +#include "config.h" +#include "macros.h" +#include "error.h" +#include "pixel.h" +#include "image.h" +#include "logcurve.h" +#include "wavelet.h" +#include "bitstream.h" +#include "stream.h" +#include "companding.h" +#include "syntax.h" +#include "codec.h" +#include "codeset.h" +#include "utilities.h" +#include "unique.h" + +#endif // COMMON_H diff --git a/source/lib/vc5_common/companding.c b/source/lib/vc5_common/companding.c new file mode 100755 index 0000000..00b0fb5 --- /dev/null +++ b/source/lib/vc5_common/companding.c @@ -0,0 +1,259 @@ +/*! @file companding.c + * + * @brief Implementation of the routines for computing the companding curves + * that is applied to the quantized coefficient magnitudes. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +//TODO: Change the companding parameter into a global constant + +#ifndef COMPANDING +#define COMPANDING 1 +#define COMPANDING_MORE (54) // Zero means no companding (54 is a good value) +#endif + + +/*! + @brief Maximum coefficient magnitude in the codebook + + @todo Need to calculate the maximum value from the codebook +*/ +const int maximum_codebook_value = 255; + + +/*! + @brief Apply the default companding curve to the specified value + + Note that this companding curve has been superceeded by the cubic curve. +*/ +int32_t CompandedValue(int32_t value) +{ + const int midpoint_rounding = 2; + + int32_t magnitude = absolute(value); + +#if COMPANDING + if (magnitude >= 40) + { + magnitude -= 40; + magnitude += midpoint_rounding; + magnitude >>= 2; + magnitude += 40; + + #if COMPANDING_MORE + if (magnitude >= COMPANDING_MORE) + { + magnitude -= COMPANDING_MORE; + magnitude += midpoint_rounding; + magnitude >>= 2; + magnitude += COMPANDING_MORE; + } +#endif + } +#endif + + // Restore the sign to the companded value + return ((value >= 0) ? magnitude : neg(magnitude)); +} + +/*! + @brief Return the parameter that controls the companding curve + + This parameter does not apply if the cubic companding curve is used. +*/ +uint32_t CompandingParameter() +{ + return COMPANDING_MORE; +} + +/*! + @brief Compute a table of values for the cubic companding curve + + The companding curve is f(x) = x + (x ^ 3 / (255 ^ 3)) * 768 + so the range of coefficient magnitudes from 0 to 255 becomess 0 to 1023. +*/ +CODEC_ERROR ComputeCubicTable(int16_t cubic_table[], int cubic_table_length, int16_t maximum_value) +{ + size_t cubic_table_size = cubic_table_length * sizeof(cubic_table[0]); + int last_cubic_table_index = cubic_table_length - 2; + int16_t last_magnitude; + int16_t index; + + // Clear the cubic table + memset(cubic_table, 0, cubic_table_size); + + for (index = 1; index <= maximum_value; index++) + { + double cubic = index; + + int magnitude = index; + + cubic *= index; + cubic *= index; + cubic *= 768; + //cubic /= 256*256*256; + cubic /= 255*255*255; + + magnitude += (int)cubic; + + //if (mag > 1023) mag = 1023; + if (magnitude > last_cubic_table_index) { + magnitude = last_cubic_table_index; + } + + cubic_table[magnitude] = index; + } + + // Fill unused entries in the cubic table + last_magnitude = 0; + for (index = 0; index < cubic_table_length; index++) + { + if (cubic_table[index]) + { + last_magnitude = cubic_table[index]; + } + else + { + cubic_table[index] = last_magnitude; + } + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Invert the companding curve applied during encoding +*/ +int32_t UncompandedValue(int32_t value) +{ +#if 1 //CUBIC_COMPANDING + double cubic; + int32_t magnitude = absolute(value); + + cubic = magnitude; + cubic *= magnitude; + cubic *= magnitude; + cubic *= 768; + //cubic /= 256*256*256; + cubic /= 255*255*255; + + magnitude += (int32_t)cubic; + + // Restore the sign + value = ((value < 0) ? neg(magnitude) : magnitude); +#else + if (40 <= value && value < 264) + { +#if COMPANDING_MORE + if (value >= COMPANDING_MORE) + { + value -= COMPANDING_MORE; + value <<= 2; + value += COMPANDING_MORE; + } +#endif + value -= 40; + value <<= 2; + value += 40; + } + else if (value <= -40) + { + // Apply the inverse companding formula to the absolute value + value = -value; + +#if COMPANDING_MORE + if (value >= COMPANDING_MORE) + { + value -= COMPANDING_MORE; + value <<= 2; + value += COMPANDING_MORE; + } +#endif + value -= 40; + value <<= 2; + value += 40; + + // Restore the sign + value = -value; + } +#endif + + return value; +} + +/*! + @brief Invert the companding curve applied to a pixel +*/ +PIXEL UncompandedPixel(PIXEL value) +{ +#if 1 //CUBIC_COMPANDING + double cubic; + int32_t magnitude = absolute(value); + + cubic = magnitude; + cubic *= magnitude; + cubic *= magnitude; + cubic *= 768; + //cubic /= 256*256*256; + cubic /= 255*255*255; + + magnitude += (int32_t)cubic; + + // Restore the sign + value = ClampPixel((value < 0) ? neg(magnitude) : magnitude); +#else + if (40 <= value && value < 264) + { +#if COMPANDING_MORE + if (value >= COMPANDING_MORE) + { + value -= COMPANDING_MORE; + value <<= 2; + value += COMPANDING_MORE; + } +#endif + value -= 40; + value <<= 2; + value += 40; + } + else if (value <= -40) + { + // Apply the inverse companding formula to the absolute value + value = neg(value); + +#if COMPANDING_MORE + if (value >= COMPANDING_MORE) + { + value -= COMPANDING_MORE; + value <<= 2; + value += COMPANDING_MORE; + } +#endif + value -= 40; + value <<= 2; + value += 40; + + // Restore the sign + value = neg(value); + } +#endif + + return value; +} + diff --git a/source/lib/vc5_common/companding.h b/source/lib/vc5_common/companding.h new file mode 100755 index 0000000..f4c00e1 --- /dev/null +++ b/source/lib/vc5_common/companding.h @@ -0,0 +1,46 @@ +/*! @file companding.h + * + * @brief Declaration of the routines for computing the companding curves + * that is applied to the quantized coefficient magnitudes. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMPANDING_H +#define COMPANDING_H + +#ifdef __cplusplus +extern "C" { +#endif + + int32_t CompandedValue(int32_t value); + + uint32_t CompandingParameter(); + + CODEC_ERROR ComputeCubicTable(int16_t cubic_table[], int cubic_table_length, int16_t maximum_value); + + // Invert the companding curve applied to a quantized coefficient magnitude (for debugging) + int32_t UncompandedValue(int32_t value); + + PIXEL UncompandedPixel(PIXEL value); + + CODEC_ERROR InvertCompanding(PIXEL *image, DIMENSION width, DIMENSION height, DIMENSION pitch); + +#ifdef __cplusplus +} +#endif + +#endif // COMPANDING_H diff --git a/source/lib/vc5_common/config.h b/source/lib/vc5_common/config.h new file mode 100755 index 0000000..5169510 --- /dev/null +++ b/source/lib/vc5_common/config.h @@ -0,0 +1,87 @@ +/*! @file config.h + * + * @brief Parameters that control the configuration of the codec. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CONFIG_H +#define CONFIG_H + +// Maximum number of layers +#define MAX_LAYER_COUNT 10 + +// Maximum number of color channels +#define MAX_CHANNEL_COUNT 4 +//const int MAX_CHANNEL_COUNT = 4; + +// Maximum number of wavelets per channel +#define MAX_WAVELET_COUNT 3 +//const int MAX_WAVELET_COUNT = 3; + +// Maximum number of bands per wavelet +#define MAX_BAND_COUNT 4 +//const int MAX_BAND_COUNT = 4; + +// Maximum number of subbands in all wavelets (including the lowpass band in the first wavelet) +#define MAX_SUBBAND_COUNT 10 + +// The number of prescale values that are encoded into the bitstream +#define MAX_PRESCALE_COUNT 8 + +//TODO: The maximum number of channels and wavelets depends on the profile + +//! Internal precision of the intermediate results after unpacking +static const int default_internal_precision = 12; + +//TODO: Change the global variable from internal_precision to encoded_precision? + +// Definitions of VC-5 parts +#define VC5_PART_ELEMENTARY 1 +#define VC5_PART_CONFORMANCE 2 // Conformance does not affect what capabilities are included +#define VC5_PART_IMAGE_FORMATS 3 +#define VC5_PART_COLOR_SAMPLING 4 +#define VC5_PART_LAYERS 5 +#define VC5_PART_SECTIONS 6 +#define VC5_PART_METADATA 7 + +// Convert a part number into a part mask +#define VC5_PART_MASK(n) (1 << ((n)-1)) + +// Define the parts supported by this codec implementation +#define VC5_ENABLED_PARTS (VC5_PART_MASK(VC5_PART_ELEMENTARY) | \ + VC5_PART_MASK(VC5_PART_IMAGE_FORMATS) | \ + VC5_PART_MASK(VC5_PART_COLOR_SAMPLING) | \ + VC5_PART_MASK(VC5_PART_SECTIONS)) + +// Compile code if any of the parts in the specified mask are supported +#define VC5_ENABLED_MASK(m) ((VC5_ENABLED_PARTS & (m)) != 0) + +// Macro for testing support for a specific part of the VC-5 standard +#define VC5_ENABLED_PART(n) (VC5_ENABLED_MASK(VC5_PART_MASK(n))) + + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + +// Maximum number of layers supported by the reference codec +#define MAX_LAYER_COUNT 10 + +#endif + +//! Number of rows of intermediate horizontal transform results +#define ROW_BUFFER_COUNT 6 + +#endif // CONFIG_H diff --git a/source/lib/vc5_common/error.h b/source/lib/vc5_common/error.h new file mode 100755 index 0000000..fbb1573 --- /dev/null +++ b/source/lib/vc5_common/error.h @@ -0,0 +1,88 @@ +/*! @file config.h + * + * @brief Definitions of the error codes reported by this codec implementation + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ERROR_H +#define ERROR_H + +/*! + @brief Codec error codes +*/ +typedef enum _codec_error +{ + CODEC_ERROR_OKAY = 0, //!< No error + CODEC_ERROR_UNEXPECTED, //!< Encountered an unexpected condition + CODEC_ERROR_OUTOFMEMORY, //!< Memory allocation failed + CODEC_ERROR_UNIMPLEMENTED, //!< Function has not been implemented + CODEC_ERROR_NULLPTR, //!< Data structure or argument pointer was null + CODEC_ERROR_BITSTREAM_SYNTAX, //!< Error in the sequence of tag value pairs + CODEC_ERROR_IMAGE_DIMENSIONS, //!< Wrong or unknown image dimensions + CODEC_ERROR_INVALID_TAG, //!< Found a tag that should not be present + CODEC_ERROR_INVALID_BAND, //!< Wavelet band index is out of range + CODEC_ERROR_DECODING_SUBBAND, //!< Error decoding a wavetet subband + CODEC_ERROR_NOTFOUND, //!< Did not find a value codeword + CODEC_ERROR_BAND_END_MARKER, //!< Could not find special codeword after end of band + CODEC_ERROR_BAND_END_TRAILER, //!< Could not find start of highpass band trailer + CODEC_ERROR_PIXEL_FORMAT, //!< Unsupported pixel format + CODEC_ERROR_INVALID_MARKER, //!< Bitstream marker was not found in the codebook + CODEC_ERROR_FILE_GET_POSITION, //!< Could not get position of the file stream + CODEC_ERROR_FILE_SEEK, //!< Could not seek to a position in a file stream + CODEC_ERROR_FILE_READ, //!< Read from a file stream failed + CODEC_ERROR_FILE_WRITE, //!< Write to a file stream failed + CODEC_ERROR_CHANNEL_SIZE_TABLE, //!< Could not write the channel size table + CODEC_ERROR_UNSUPPORTED_FORMAT, //!< Pixel or encoded format is not supported + CODEC_ERROR_MISSING_START_MARKER, //!< Bitstream does not begin with the start marker + CODEC_ERROR_DUPLICATE_HEADER_PARAMETER, //!< Header parameter occurs more than once + CODEC_ERROR_REQUIRED_PARAMETER, //!< Optional tag-value pair for a required parameter + CODEC_ERROR_LOWPASS_PRECISION, //!< Number of bits per lowpass coefficient out of range + CODEC_ERROR_LOWPASS_VALUE, //!< Lowpass coefficient value is out of range + CODEC_ERROR_IMAGE_TYPE, //!< Could not determine the characteristics of the input image + CODEC_ERROR_BAD_IMAGE_FORMAT, //!< Bad image format (VC-5 Part 3 only) + CODEC_ERROR_PATTERN_DIMENSIONS, //!< Bad pattern dimensions (VC-5 Part 3 only) + CODEC_ERROR_ENABLED_PARTS, //!< Incorrect enabled parts of the VC-5 standard + CODEC_ERROR_SYNTAX_ERROR, //!< Unspecified error in the bitstream syntax + CODEC_ERROR_UMID_LABEL, //!< Incorrect UMID label + CODEC_ERROR_BAD_SECTION_TAG, //!< The specified tag does not correspond to a section header + + + /***** Reserve a block of error codes for the bitstream *****/ + + CODEC_ERROR_BITSTREAM = (1 << 10), //!< Block of bitstream error codes + + + /***** Reserve a block of error codes for the calling application *****/ + + CODEC_ERROR_APPLICATION = (16 << 10), //!< Block of error codes for the application + CODEC_ERROR_MISSING_ARGUMENT, //!< Program did not have enough arguments + CODEC_ERROR_BAD_ARGUMENT, //!< Invalid value for one of the arguments + CODEC_ERROR_OPEN_FILE_FAILED, //!< Could not open the file for reading + CODEC_ERROR_CREATE_FILE_FAILED, //!< Could not open the file for writing + CODEC_ERROR_UNSUPPORTED_FILE_TYPE, //!< The output file type is not supported + CODEC_ERROR_FILE_SIZE_FAILED, //!< Could not determine the size of the file + CODEC_ERROR_READ_FILE_FAILED, //!< Could not read a file + CODEC_ERROR_FILE_WRITE_FAILED, //!< Could not write to the file + CODEC_ERROR_FILE_FLUSH_FAILED, //!< Could not flush the file buffer + CODEC_ERROR_PARSE_ARGUMENTS, //!< Error while parsing the command-line arguments + CODEC_ERROR_USAGE_INFO, //!< User asked for help with program usage + CODEC_ERROR_BANDFILE_FAILED, //!< Could not write the bandfile + CODEC_ERROR_BAD_PARAMETER, //!< Missing or inconsistent parameters + +} CODEC_ERROR; + +#endif // ERROR_H diff --git a/source/lib/vc5_common/image.c b/source/lib/vc5_common/image.c new file mode 100755 index 0000000..0af14fe --- /dev/null +++ b/source/lib/vc5_common/image.c @@ -0,0 +1,321 @@ +/*! @file image.c + * + * @brief Implementation of the data structure for the image that is + * input to the image unpacking process. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +/*! + @brief Initialize the fields in an image data structure + This routine is the constructor for the image data type that + initializes an image instance to default values. + */ +CODEC_ERROR InitImage(IMAGE *image) +{ + if (image != NULL) + { + image->width = 0; + image->height = 0; + image->pitch = 0; + image->offset = 0; + image->format = PIXEL_FORMAT_UNKNOWN; + image->buffer = NULL; + image->size = 0; + return CODEC_ERROR_OKAY; + } + + return CODEC_ERROR_NULLPTR; +} + +CODEC_ERROR InitRGBImage(RGB_IMAGE *image) +{ + if (image != NULL) + { + image->width = 0; + image->height = 0; + image->pitch = 0; + image->buffer = NULL; + image->size = 0; + return CODEC_ERROR_OKAY; + } + + return CODEC_ERROR_NULLPTR; +} +/*! + @brief Allocate the buffer for a image with the sepcified dimensions and format. + This routine calculates the image pitch from the width and format, rounding up + the pitch to satisfy memory alignment requirements in the codec. + This routine will cause a memory leak if it is called with a image that has + already been allocated. + */ +CODEC_ERROR AllocImage(gpr_allocator *allocator, IMAGE *image, DIMENSION width, DIMENSION height, PIXEL_FORMAT format) +{ + size_t size = 0; + DIMENSION pitch = 0; + + assert(image != NULL); + + // Compute the pitch from the width and format + pitch = ImagePitch(width, format); + assert(pitch > 0); + +#if VC5_ENABLED_PART(VC5_PART_COLOR_SAMPLING) + size = height * pitch; +#else + size = height * pitch; +#endif + assert(size > 0); + + // Allocate the image buffer + image->buffer = allocator->Alloc(size); + if (image->buffer != NULL) + { + image->width = width; + image->height = height; + image->pitch = pitch; + image->format = format; + image->offset = 0; + image->size = size; + return CODEC_ERROR_OKAY; + } + + return CODEC_ERROR_OUTOFMEMORY; +} + +/*! + @brief Deallocate the buffer in a image data structure + This routine does not deallocate the image data structure itself. + In the typical use case, the image data structure is allocated on + the stack and teh decoder allocates the buffer for the output image + after determining the size of the image. The image data structure + is deallocated automatically by a calling routine. + */ +CODEC_ERROR ReleaseImage(gpr_allocator *allocator, IMAGE *image) +{ + allocator->Free(image->buffer); + return CODEC_ERROR_OKAY; +} + +/*! + @brief Compute the image pitch for the specified width and pixel format + The pitch of the image (in bytes) is computed for a image with the width + and pixel format that is specified. The pitch is rounted up to satisfy + memory alignment constraints required by the codec. + The return value is zero if the pitch could not be computed for the + specified width and format. + */ +DIMENSION ImagePitch(DIMENSION width, PIXEL_FORMAT format) +{ + DIMENSION pitch = 0; + + switch (format) + { + case PIXEL_FORMAT_RAW_RGGB_14: + case PIXEL_FORMAT_RAW_GBRG_12: + case PIXEL_FORMAT_RAW_GBRG_12P: + case PIXEL_FORMAT_RAW_RGGB_16: + // Half the width of the image times 2 samples times 2 bytes per sample + pitch = width * sizeof(uint16_t); + break; + + default: + assert(0); + } + + return pitch; +} + +/*! + @brief Set the dimensions and pixel format of a image + This routine is used to set the dimensions and format of a image + that was allocated with unknown parameters. + */ +CODEC_ERROR SetImageFormat(IMAGE *image, + DIMENSION width, + DIMENSION height, + DIMENSION pitch, + PIXEL_FORMAT format, + size_t offset) +{ + assert(image != NULL); + + image->width = width; + image->height = height; + image->pitch = pitch; + image->format = format; + image->offset = offset; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Return the address of the image in the buffer + This routine should be used to obtain the starting address of + a image in a image buffer since the image may be offset from the + beginning of the buffer. + */ +void *ImageData(IMAGE *image) +{ + uint8_t *buffer = image->buffer; + buffer += image->offset; + return buffer; +} + +/*! + @brief Return the address of the specified row in the image + The routine returns NULL if the image argument is null or the + specified row is out of bounds. + */ +void *RowAddress(IMAGE *image, DIMENSION row) +{ + size_t pitch = image->pitch; + + if (image != NULL && image->pitch != 0) + { + //if (0 <= row && row < image->height) + if (row < image->height) + { + void *address = (void *)((uintptr_t)ImageData(image) + row * pitch); + return address; + } + } + + // Could not compute the address of a valid row + return NULL; +} + +CODEC_ERROR ReleaseComponentArrays(gpr_allocator *allocator, + UNPACKED_IMAGE *image, + int channel_count ) +{ + int channel; + + for (channel = 0; channel < channel_count; channel++) + { + allocator->Free( image->component_array_list[channel].data ); + } + + allocator->Free( image->component_array_list ); + + return CODEC_ERROR_OKAY; +} + +CODEC_ERROR AllocateComponentArrays(gpr_allocator *allocator, + UNPACKED_IMAGE *image, + int channel_count, + DIMENSION max_channel_width, + DIMENSION max_channel_height, + PIXEL_FORMAT format, + int bits_per_component) +{ + int channel; + + // Allocate the vector of component arrays + size_t size = channel_count * sizeof(COMPONENT_ARRAY); + image->component_array_list = allocator->Alloc(size); + if (image->component_array_list == NULL) { + return CODEC_ERROR_OUTOFMEMORY; + } + + // Clear the component array information so that the state is consistent + image->component_count = 0; + memset(image->component_array_list, 0, size); + + // Initialize each component array + for (channel = 0; channel < channel_count; channel++) + { + DIMENSION channel_width = max_channel_width; + DIMENSION channel_height = max_channel_height; + + // Allocate space for the data in the component array + CODEC_ERROR error = AllocateComponentArray( allocator, &image->component_array_list[channel], channel_width, channel_height, (uint_least8_t)bits_per_component ); + + if( error != CODEC_ERROR_OKAY ) { + return error; + } + } + + // Set the number of component arrays + image->component_count = channel_count; + + return CODEC_ERROR_OKAY; +} + +CODEC_ERROR AllocateComponentArray(gpr_allocator *allocator, + COMPONENT_ARRAY *component_array, + DIMENSION width, + DIMENSION height, + PRECISION bits_per_component) +{ + //COMPONENT_ARRAY *component_array = Alloc(allocator, sizeof(COMPONENT_ARRAY)); + + // Allocate space for the data in the component array + size_t pitch = width * sizeof(COMPONENT_VALUE); + size_t size = height * pitch; + void *buffer = allocator->Alloc(size); + assert(buffer != NULL); + if (! (buffer != NULL)) { + return CODEC_ERROR_OUTOFMEMORY; + } + + component_array->width = width; + component_array->height = height; + component_array->pitch = pitch; + component_array->data = buffer; + component_array->bits_per_component = bits_per_component; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Initialize the data structure for the unpacked image + Note that the component arrays will be allocated after the bitstream + has been decoded and the dimensions of the component arrys are known. + */ +CODEC_ERROR InitUnpackedImage(UNPACKED_IMAGE *unpacked_image) +{ + if (unpacked_image == NULL) { + return CODEC_ERROR_UNEXPECTED; + } + + // Clear the fields in the unpacked iamge + memset(unpacked_image, 0, sizeof(UNPACKED_IMAGE)); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Return the maximum number of bits per component across all channels in the unpacked image + */ +PRECISION MaxBitsPerComponent(const UNPACKED_IMAGE *image) +{ + PRECISION max_bits_per_component = 0; + int channel; + + for (channel = 0; channel < image->component_count; channel++) + { + COMPONENT_ARRAY *component_array = &image->component_array_list[channel]; + + if (max_bits_per_component < component_array->bits_per_component) { + max_bits_per_component = component_array->bits_per_component; + } + } + + return max_bits_per_component; +} diff --git a/source/lib/vc5_common/image.h b/source/lib/vc5_common/image.h new file mode 100755 index 0000000..4739727 --- /dev/null +++ b/source/lib/vc5_common/image.h @@ -0,0 +1,165 @@ +/*! @file image.h + * + * @brief Declaration of structures and functions for images + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef IMAGE_H +#define IMAGE_H + +//! Data type for the values in a component array +typedef uint16_t COMPONENT_VALUE; + +/*! + @brief Data structure for an image input to the unpacking process + + This data structure is used to represent the image that is the input to the + image unpacking process that unpacks an image into component arrays for encoding. + Unlike the wavelet data structures, an image contains multiple color components, + usually in a packed pixel format. +*/ +typedef struct _packed_image +{ + DIMENSION width; //!< Width of the frame (in pixels) + DIMENSION height; //!< Height of the frame + size_t pitch; //!< Distance between rows (in bytes) + PIXEL_FORMAT format; //!< Format of the pixels + void *buffer; //!< Address of the buffer for the frame + size_t size; //!< Allocated size of the buffer (in bytes) + size_t offset; //!< Offset to the start of the frame +} PACKED_IMAGE; + +/*! + @brief Data structure for an image input to the unpacking process + + This data structure is used to represent the image that is the input to the + image unpacking process that unpacks an image into component arrays for encoding. + Unlike the wavelet data structures, an image contains multiple color components, + usually in a packed pixel format. + */ +typedef struct _rgb_image +{ + DIMENSION width; //!< Width of the frame (in pixels) + DIMENSION height; //!< Height of the frame + size_t pitch; //!< Distance between rows (in bytes) + void *buffer; //!< Address of the buffer for the frame + size_t size; //!< Allocated size of the buffer (in bytes) +} RGB_IMAGE; + +//! Short name for the packed image data type +typedef PACKED_IMAGE IMAGE; + +/*! + @brief Data structure for an array that contains a single type of component + + This data structure is used to represent the component array output by the image + unpacking process. The image unpacking process unpacks an image into component + arrays for encoding. +*/ +typedef struct _component_array +{ + DIMENSION width; //!< Width of the frame (in pixels) + DIMENSION height; //!< Height of the frame + size_t pitch; //!< Distance between rows (in bytes) + COMPONENT_VALUE *data; //!< Buffer for the array of component values + + //! Number of bits per in each component value + PRECISION bits_per_component; + +} COMPONENT_ARRAY; + +/*! + @brief Image represented as an ordered set of component arrays + + The decoder outputs a set of component arrays that represent an image. + + The image repacking process can pack the component arrays output by the + decoder into a packed image. +*/ +typedef struct _unpacked_image +{ + //! Number of component arrays in the unpacked image + int component_count; + + //! Vector of component arrays + COMPONENT_ARRAY *component_array_list; + +} UNPACKED_IMAGE; + +/*! + @brief Flags that describe the image structure +*/ +typedef enum +{ + IMAGE_STRUCTURE_INTERLACED = 0x0001, //!< Set the first bit if the image is interlaced + IMAGE_STRUCTURE_BOTTOM_FIELD_FIRST = 0x0002, //!< The bottom field is encoded before the top field + IMAGE_STRUCTURE_BOTTOM_ROW_FIRST = 0x0010, //!< The encoded image is upside down +} IMAGE_STRUCTURE; + + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR InitImage(IMAGE *image); + + CODEC_ERROR InitRGBImage(RGB_IMAGE *image); + + CODEC_ERROR AllocImage(gpr_allocator *allocator, IMAGE *image, DIMENSION width, DIMENSION height, PIXEL_FORMAT format); + + CODEC_ERROR ReleaseImage(gpr_allocator *allocator, IMAGE *image); + + DIMENSION ImagePitch(DIMENSION width, PIXEL_FORMAT format); + + CODEC_ERROR SetImageFormat(IMAGE *image, + DIMENSION width, + DIMENSION height, + DIMENSION pitch, + PIXEL_FORMAT format, + size_t offset); + + void *ImageData(IMAGE *image); + + void *RowAddress(IMAGE *image, DIMENSION row); + + CODEC_ERROR ReleaseComponentArrays(gpr_allocator *allocator, + UNPACKED_IMAGE *image, + int channel_count ); + + CODEC_ERROR AllocateComponentArrays(gpr_allocator *allocator, + UNPACKED_IMAGE *image, + int channel_count, + DIMENSION max_channel_width, + DIMENSION max_channel_height, + PIXEL_FORMAT format, + int bits_per_component); + + CODEC_ERROR AllocateComponentArray(gpr_allocator *allocator, + COMPONENT_ARRAY *component_array, + DIMENSION width, + DIMENSION height, + PRECISION bits_per_component); + + CODEC_ERROR InitUnpackedImage(UNPACKED_IMAGE *image); + + PRECISION MaxBitsPerComponent(const UNPACKED_IMAGE *image); + +#ifdef __cplusplus +} +#endif + +#endif // IMAGE_H diff --git a/source/lib/vc5_common/logcurve.c b/source/lib/vc5_common/logcurve.c new file mode 100755 index 0000000..695aa5a --- /dev/null +++ b/source/lib/vc5_common/logcurve.c @@ -0,0 +1,58 @@ +/*! @file logcurve.c + * + * @brief Implementation of functions used to do log conversion. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +uint16_t EncoderLogCurve[LOG_CURVE_TABLE_LENGTH]; + +uint16_t DecoderLogCurve[LOG_CURVE_TABLE_LENGTH]; + +void SetupDecoderLogCurve() +{ + int i; + const int log_table_size = sizeof(DecoderLogCurve) / sizeof(DecoderLogCurve[0]); + + const int max_16_bit = (1 << 16) - 1; + + for( i = 0; i < log_table_size; i++ ) + { + //input 12-bit, output 16-bit + float input = i; + float output = max_16_bit * (pow(113.0, input/4095.0) - 1.0)/112.0; + + DecoderLogCurve[i] = minimum( (int)output, max_16_bit ); + } +} + +void SetupEncoderLogCurve() +{ + int i; + const int max_input_val = LOG_CURVE_TABLE_LENGTH - 1; + + for( i = 0; i < LOG_CURVE_TABLE_LENGTH; i++ ) + { + //input 16-bit, output 12-bit + float input = maximum( 0, i ); + float output = 4095.0 * (log10(input/max_input_val * 112.0 + 1.0)/log10(113)); + + EncoderLogCurve[i] = ( (uint16_t)output ); + } +} + diff --git a/source/lib/vc5_common/logcurve.h b/source/lib/vc5_common/logcurve.h new file mode 100755 index 0000000..ac03425 --- /dev/null +++ b/source/lib/vc5_common/logcurve.h @@ -0,0 +1,42 @@ +/*! @file logcurve.h + * + * @brief Declaration of the data structures and constants used to do log conversion. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef LOGCURVE_H +#define LOGCURVE_H + +#define LOG_CURVE_TABLE_LENGTH (1 << 12) + +#ifdef __cplusplus +extern "C" { +#endif + + extern uint16_t EncoderLogCurve[]; + + extern uint16_t DecoderLogCurve[]; + + void SetupDecoderLogCurve(); + + void SetupEncoderLogCurve(); + +#ifdef __cplusplus +} +#endif + +#endif // LOGCURVE_H diff --git a/source/lib/vc5_common/pixel.h b/source/lib/vc5_common/pixel.h new file mode 100755 index 0000000..7c3b9e0 --- /dev/null +++ b/source/lib/vc5_common/pixel.h @@ -0,0 +1,100 @@ +/*! @file pixel.h + * + * @brief The pixel format enumerations define the pixel packing formats that are + * supported by the codec for input to the image unpacking process and for + * output from the image repacking process. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PIXEL_H +#define PIXEL_H + +//! Data type for pixels +typedef int16_t PIXEL; + +//! Minimum and maximum pixel values +enum { + PIXEL_MIN = INT16_MIN, + PIXEL_MAX = INT16_MAX, +}; + +//! Alternative definition for wavelet coefficients +typedef int16_t COEFFICIENT; + +//! Minimum and maximum coefficient values +enum +{ + COEFFICIENT_MIN = INT16_MIN, + COEFFICIENT_MAX = INT16_MAX, +}; + +/*! + @brief Pixels formats supported by the codec + + The pixel format is only the packing arrangement for color components and + does not specify whether the image is interlaced or the bottom row is first. + + @todo Need to add support for more pixel formats to the reference decoder +*/ +typedef enum +{ + PIXEL_FORMAT_UNKNOWN = 0, + + PIXEL_FORMAT_RAW_RGGB_16 = 104, + + PIXEL_FORMAT_RAW_RGGB_12 = 106, + PIXEL_FORMAT_RAW_RGGB_12P = 107, + PIXEL_FORMAT_RAW_RGGB_14 = 108, + + PIXEL_FORMAT_RAW_GBRG_12 = 109, + PIXEL_FORMAT_RAW_GBRG_12P = 110, + PIXEL_FORMAT_RAW_GBRG_14 = 111, + + PIXEL_FORMAT_RAW_DEFAULT = PIXEL_FORMAT_RAW_RGGB_14, + + //! Input pixel formats above this value must be encoded into the sample + PIXEL_FORMAT_TAG_REQUIRED = 100, + +} PIXEL_FORMAT; + + +#ifdef __cplusplus +extern "C" { +#endif + +/*! +@brief Force a pixel value to be in range +*/ +STATIC_INLINE PIXEL ClampPixel(int32_t value) +{ + // Check for values that are outside the range (for debugging) + assert(PIXEL_MIN <= value && value <= PIXEL_MAX); + + if (value < PIXEL_MIN) + value = PIXEL_MIN; + else + if (value > PIXEL_MAX) + value = PIXEL_MAX; + + return (PIXEL)value; +} + +#ifdef __cplusplus +} +#endif + +#endif // PIXEL_H diff --git a/source/lib/vc5_common/stream.c b/source/lib/vc5_common/stream.c new file mode 100755 index 0000000..573b673 --- /dev/null +++ b/source/lib/vc5_common/stream.c @@ -0,0 +1,524 @@ +/*! @file stream.h + * + * @brief This module implements a byte stream abstraction that hides the details + * of how a stream of bytes is read or written on demand by the bitstream. + * The byte stream can be bound to a binary file opened for reading (writing), + * to a buffer in memory, or to a module that reads (writes) a video track + * in a media container. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +// Local functions +CODEC_ERROR GetBlockFile(STREAM *stream, void *buffer, size_t size, size_t offset); +CODEC_ERROR PutBlockFile(STREAM *stream, void *buffer, size_t size, size_t offset); +CODEC_ERROR GetBlockMemory(STREAM *stream, void *buffer, size_t size, size_t offset); +CODEC_ERROR PutBlockMemory(STREAM *stream, void *buffer, size_t size, size_t offset); + +/*! + @brief Open a stream for reading bytes from the specified file + +*/ +CODEC_ERROR OpenStream(STREAM *stream, const char *pathname) +{ + assert(stream != NULL); + if (! (stream != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + // Clear all members of the stream data structure + memset(stream, 0, sizeof(STREAM)); + + // Open the file and bind it to the stream + stream->location.file.iobuf = fopen(pathname, "rb"); + assert(stream->location.file.iobuf != NULL); + if (! (stream->location.file.iobuf != NULL)) { + return CODEC_ERROR_OPEN_FILE_FAILED; + } + + // Set the stream type and access + stream->type = STREAM_TYPE_FILE; + stream->access = STREAM_ACCESS_READ; + + // Clear the number of bytes read from the stream + stream->byte_count = 0; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Create a stream for writing bytes to a specified file + +*/ +CODEC_ERROR CreateStream(STREAM *stream, const char *pathname) +{ + assert(stream != NULL); + if (! (stream != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + // Clear all members of the stream data structure + memset(stream, 0, sizeof(STREAM)); + + // Open the file and bind it to the stream + stream->location.file.iobuf = fopen(pathname, "wb+"); + assert(stream->location.file.iobuf != NULL); + if (! (stream->location.file.iobuf != NULL)) { + return CODEC_ERROR_CREATE_FILE_FAILED; + } + + // Set the stream type and access + stream->type = STREAM_TYPE_FILE; + stream->access = STREAM_ACCESS_WRITE; + + // Clear the number of bytes written to the stream + stream->byte_count = 0; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Read a word from a byte stream + + This routine is used by the bitstream to read a word from a byte stream. + A word is the number of bytes that can be stored in the internal buffer + used by the bitstream. + + @todo Need to modify the routine to return an indication of end of file + or an error reading from the byte stream. +*/ +BITWORD GetWord(STREAM *stream) +{ + BITWORD buffer = 0; + size_t bytes_read = sizeof(buffer); + + assert(stream != NULL); + + switch (stream->type) + { + case STREAM_TYPE_FILE: + bytes_read = fread(&buffer, 1, sizeof(buffer), stream->location.file.iobuf); + assert(bytes_read == sizeof(buffer)); + break; + + case STREAM_TYPE_MEMORY: + memcpy(&buffer, (uint8_t *)stream->location.memory.buffer + stream->byte_count, sizeof(buffer)); + break; + + default: + assert(0); + break; + } + + if (bytes_read > 0) + stream->byte_count += sizeof(buffer); + + return buffer; +} + +/*! + @brief Read a byte from a byte stream +*/ +uint8_t GetByte(STREAM *stream) +{ + assert(stream != NULL); + int byte = 0; + + switch (stream->type) + { + case STREAM_TYPE_FILE: + byte = fgetc(stream->location.file.iobuf); + break; + + case STREAM_TYPE_MEMORY: + byte = ((uint8_t *)stream->location.memory.buffer)[stream->byte_count]; + break; + + default: + assert(0); + break; + } + + stream->byte_count++; + assert(byte >= 0 && (byte & ~0xFF) == 0); + + return (uint8_t)byte; +} + +/*! + @brief Write a word to a byte stream + + This routine is used by the bitstream to write a word to a byte stream. + A word is the number of bytes that can be stored in the internal buffer + used by the bitstream. + + @todo Need to modify the routine to return an indication of an error + writing to the byte stream. +*/ +CODEC_ERROR PutWord(STREAM *stream, BITWORD word) +{ + size_t written; + + word = Swap32(word); + + assert(stream != NULL); + + switch (stream->type) + { + case STREAM_TYPE_FILE: + written = fwrite(&word, sizeof(word), 1, stream->location.file.iobuf); + if (written == 0) + return CODEC_ERROR_FILE_WRITE_FAILED; + break; + + case STREAM_TYPE_MEMORY: + { + uint8_t* buffer = (uint8_t *)stream->location.memory.buffer + stream->byte_count; + + memcpy(buffer, &word, sizeof(word)); + } + break; + + default: + assert(0); + break; + } + + stream->byte_count += sizeof(word); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write a byte to a byte stream +*/ +CODEC_ERROR PutByte(STREAM *stream, uint8_t byte) +{ + assert(stream != NULL); + + //assert(byte >= 0 && (byte & ~0xFF) == 0); + + switch (stream->type) + { + case STREAM_TYPE_FILE: + if (fputc(byte, stream->location.file.iobuf) == EOF) + return CODEC_ERROR_FILE_WRITE_FAILED; + break; + + case STREAM_TYPE_MEMORY: + ((uint8_t *)stream->location.memory.buffer)[stream->byte_count] = byte; + break; + + default: + assert(0); + break; + } + + stream->byte_count++; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Rewind the stream to the beginning of the buffer or file +*/ +CODEC_ERROR RewindStream(STREAM *stream) +{ + assert(stream != NULL); + if (! (stream != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + if( stream->type == STREAM_TYPE_FILE ) + { + if (stream->location.file.iobuf != NULL) { + assert(fseek(stream->location.file.iobuf, 0, SEEK_SET) == 0); + return CODEC_ERROR_BITSTREAM; + } + } + + stream->byte_count = 0; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Skip the specified number of bytes in the stream +*/ +CODEC_ERROR SkipBytes(STREAM *stream, size_t size) +{ + for (; size > 0; size--) + { + (void)GetByte(stream); + } + return CODEC_ERROR_OKAY; +} + +/*! + @brief Pad the specified number of bytes in the stream +*/ +CODEC_ERROR PadBytes(STREAM *stream, size_t size) +{ + const uint8_t byte = 0; + for (; size > 0; size--) + { + PutByte(stream, byte); + } + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write the stream buffer to the file +*/ +CODEC_ERROR FlushStream(STREAM *stream) +{ + assert(stream != NULL); + if (! (stream != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + if (stream->type == STREAM_TYPE_FILE) + { + int result = fflush(stream->location.file.iobuf); + if (result != 0) { + return CODEC_ERROR_FILE_FLUSH_FAILED; + } + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Create a byte stream for reading from a memory location + */ +CODEC_ERROR OpenStreamBuffer(STREAM *stream, void *buffer, size_t size) +{ + assert(stream != NULL); + if (! (stream != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + // Clear all members of the stream data structure + memset(stream, 0, sizeof(STREAM)); + + // Bind the stream to the buffer + stream->location.memory.buffer = buffer; + stream->location.memory.size = size; + + // Set the stream type and access + stream->type = STREAM_TYPE_MEMORY; + stream->access = STREAM_ACCESS_READ; + + // Clear the number of bytes written to the stream + stream->byte_count = 0; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Create a byte stream for writing to a memory location +*/ +CODEC_ERROR CreateStreamBuffer(STREAM *stream, void *buffer, size_t size) +{ + assert(stream != NULL); + if (! (stream != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + // Clear all members of the stream data structure + memset(stream, 0, sizeof(STREAM)); + + // Bind the stream to the buffer + stream->location.memory.buffer = buffer; + stream->location.memory.size = size; + + // Set the stream type and access + stream->type = STREAM_TYPE_MEMORY; + stream->access = STREAM_ACCESS_WRITE; + + // Clear the number of bytes written to the stream + stream->byte_count = 0; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Return the starting address and number of bytes in a buffer + + This routine is used to get the address and count of the bytes written + to a memory stream (buffer). +*/ +CODEC_ERROR GetStreamBuffer(STREAM *stream, void **buffer_out, size_t *size_out) +{ + assert(stream != NULL); + if (! (stream != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + assert(stream->type == STREAM_TYPE_MEMORY); + + if (buffer_out != NULL) { + *buffer_out = stream->location.memory.buffer; + } + + if (size_out != NULL) { + *size_out = stream->byte_count; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Read a block of data at the specified offset in the byte stream +*/ +CODEC_ERROR GetBlock(STREAM *stream, void *buffer, size_t size, size_t offset) +{ + switch (stream->type) + { + case STREAM_TYPE_FILE: + return GetBlockFile(stream, buffer, size, offset); + break; + + case STREAM_TYPE_MEMORY: + return GetBlockMemory(stream, buffer, size, offset); + break; + + case STREAM_TYPE_UNKNOWN: + assert(0); + break; + } + + return CODEC_ERROR_UNEXPECTED; +} + +/*! + @brief Read a block of data from a file stream +*/ +CODEC_ERROR GetBlockFile(STREAM *stream, void *buffer, size_t size, size_t offset) +{ + FILE *file = stream->location.file.iobuf; + fpos_t position; + + (void)file; + (void)position; + + // Save the current position in the file + if (fgetpos(file, &position) != 0) { + return CODEC_ERROR_FILE_GET_POSITION; + } + + // Seek to the specified offset + assert(0 <= offset && offset <= LONG_MAX); + if (fseek(file, (long)offset, SEEK_SET) != 0) { + return CODEC_ERROR_FILE_SEEK; + } + + // Read data from the file + if (fread(buffer, size, 1, file) != 1) { + return CODEC_ERROR_FILE_READ; + } + + // Return to the previous position in the file + // if (fseek(file, (long)position, SEEK_SET) != 0) { + if (fsetpos(file, &position) != 0) { + return CODEC_ERROR_FILE_SEEK; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Read a block of data from a memory stream (buffer) +*/ +CODEC_ERROR GetBlockMemory(STREAM *stream, void *buffer, size_t size, size_t offset) +{ + uint8_t *block = (uint8_t *)stream->location.memory.buffer + offset; + memcpy(buffer, block, size); + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write a block of data at the specified offset in the byte stream +*/ +CODEC_ERROR PutBlock(STREAM *stream, void *buffer, size_t size, size_t offset) +{ + switch (stream->type) + { + case STREAM_TYPE_FILE: + return PutBlockFile(stream, buffer, size, offset); + break; + + case STREAM_TYPE_MEMORY: + return PutBlockMemory(stream, buffer, size, offset); + break; + + case STREAM_TYPE_UNKNOWN: + assert(0); + break; + } + + return CODEC_ERROR_UNEXPECTED; +} + +/*! + @brief Write a block of data at the specified offset in a file stream +*/ +CODEC_ERROR PutBlockFile(STREAM *stream, void *buffer, size_t size, size_t offset) +{ + FILE *file = stream->location.file.iobuf; + fpos_t position; + + (void)file; + (void)position; + + // Save the current position in the file + if (fgetpos(file, &position) != 0) { + return CODEC_ERROR_FILE_GET_POSITION; + } + + // Seek to the specified offset and write to the file + assert(0 <= offset && offset <= LONG_MAX); + if (fseek(file, (long)offset, SEEK_SET) != 0) { + return CODEC_ERROR_FILE_SEEK; + } + + // Write data to the file + if (fwrite(buffer, size, 1, file) != 1) { + return CODEC_ERROR_FILE_WRITE; + } + + // Return to the previous position in the file + // if (fseek(file, (long)position, SEEK_SET) != 0) { + if (fsetpos(file, &position) != 0) { + return CODEC_ERROR_FILE_SEEK; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write a block of data at the specified offset in a memory stream +*/ +CODEC_ERROR PutBlockMemory(STREAM *stream, void *buffer, size_t size, size_t offset) +{ + uint8_t *block = (uint8_t *)stream->location.memory.buffer + offset; + memcpy(block, buffer, size); + return CODEC_ERROR_OKAY; +} + + diff --git a/source/lib/vc5_common/stream.h b/source/lib/vc5_common/stream.h new file mode 100755 index 0000000..df70a4f --- /dev/null +++ b/source/lib/vc5_common/stream.h @@ -0,0 +1,133 @@ +/*! @file stream.h + * + * @brief The stream abstracts the methods used by bitstreams to output bytes + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef STREAM_H +#define STREAM_H + +//! Type of stream (binary file or memory buffer) +typedef enum _stream_type +{ + STREAM_TYPE_UNKNOWN = 0, //!< Unknown type of stream + STREAM_TYPE_FILE, //!< Simple binary file + STREAM_TYPE_MEMORY, //!< Buffer in memory + +} STREAM_TYPE; + +/*! + @brief Stream access (read or write) + + The stream provided with the reference decoder only supports + read access. +*/ +typedef enum _stream_access +{ + STREAM_ACCESS_UNKNOWN = 0, + STREAM_ACCESS_READ, + STREAM_ACCESS_WRITE, + +} STREAM_ACCESS; + + +/*! + @brief Declaration of the data structure for a byte stream + + The byte stream encapsulates the location of encoded images and the + means for reading (writing) encoded images samples. The byte stream + could be a binary file that has been opened for reading (writing) or + a buffer in memory. The reference codec uses a binary file as the byte + stream so the functionality for streams attached to memory buffers has + not been tested. + + It is intended that the byte stream can be enhanced to read (write) + encoded images from (into) a track in a media container. +*/ +#define STREAM_CACHE_SIZE 16 +typedef struct _stream +{ + STREAM_TYPE type; //!< Type of stream (file or memory buffer) + STREAM_ACCESS access; //!< Type of access (read or write) + + //! Union of parameters for different types of streams + union _location + { + //! Parameters for a binary file stream + struct _file + { + FILE *iobuf; //!< Binary file that contains the stream + BITWORD cache[STREAM_CACHE_SIZE]; + int cache_index; + + } file; //!< Parameters for a stream in a binary file + + //! Parameters for a stream bound to a memory buffer + struct _memory + { + void *buffer; //!< Memory buffer that contains the stream + size_t size; //!< Length of the stream (in bytes) + + } memory; //!< Parameters for a stream in a memory buffer + + //TODO: Add other stream types for media containers + + } location; //!< Location of the byte stream (file or memory buffer) + + size_t byte_count; //!< Number of bytes read or written to the stream + +} STREAM; + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR OpenStream(STREAM *stream, const char *pathname); + + CODEC_ERROR CreateStream(STREAM *stream, const char *pathname); + + CODEC_ERROR RewindStream(STREAM *stream); + + BITWORD GetWord(STREAM *stream); + + uint8_t GetByte(STREAM *stream); + + CODEC_ERROR SkipBytes(STREAM *stream, size_t size); + + CODEC_ERROR PutWord(STREAM *stream, BITWORD word); + + CODEC_ERROR PutByte(STREAM *stream, uint8_t byte); + + CODEC_ERROR PadBytes(STREAM *stream, size_t size); + + CODEC_ERROR FlushStream(STREAM *stream); + + CODEC_ERROR OpenStreamBuffer(STREAM *stream, void *buffer, size_t size); + + CODEC_ERROR CreateStreamBuffer(STREAM *stream, void *buffer, size_t size); + + CODEC_ERROR GetStreamBuffer(STREAM *stream, void **buffer_out, size_t *size_out); + + CODEC_ERROR GetBlock(STREAM *stream, void *buffer, size_t size, size_t offset); + + CODEC_ERROR PutBlock(STREAM *stream, void *buffer, size_t size, size_t offset); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/lib/vc5_common/syntax.c b/source/lib/vc5_common/syntax.c new file mode 100755 index 0000000..bf64afe --- /dev/null +++ b/source/lib/vc5_common/syntax.c @@ -0,0 +1,150 @@ +/*! @file syntax.c + * + * @brief Implementation of functions for parsing the bitstream syntax of encoded samples. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +//TODO: Simplify the definition of the marker bits +#define CODEC_LOWPASS_START_CODE 0x1A4A +#define CODEC_LOWPASS_START_SIZE 16 +#define CODEC_LOWPASS_END_CODE 0x1B4B +#define CODEC_LOWPASS_END_SIZE 16 + +#define CODEC_HIGHPASS_START_CODE 0x0D0D +#define CODEC_HIGHPASS_START_SIZE 16 +#define CODEC_HIGHPASS_END_CODE 0x0C0C +#define CODEC_HIGHPASS_END_SIZE 16 + +#define CODEC_BAND_START_CODE 0x0E0E +#define CODEC_BAND_START_SIZE 16 +//#define CODEC_BAND_END_CODE 0x038F0B3E //Codeset dependent cs9 +//#define CODEC_BAND_END_SIZE 26 //Codeset dependent cs9 +#define CODEC_BAND_END_CODE 0x0000E33F //Codeset dependent cs15 +#define CODEC_BAND_END_SIZE 16 //Codeset dependent cs15 + +#define CODEC_SAMPLE_STOP_CODE 0x1E1E +#define CODEC_SAMPLE_STOP_SIZE 16 + +#define CODEC_COEFFICIENT_START_CODE 0x0F0F +#define CODEC_COEFFICIENT_START_SIZE 16 + +//! Size of a tag or value (in bits) +#define BITSTREAM_TAG_SIZE 16 + + +// Bits in the interlace structure flags + +#define CODEC_FLAGS_INTERLACED 0x01 //!< Interlaced flags +#define CODEC_FLAGS_FIELD1_FIRST 0x02 //!< NTSC has this bit cleared +#define CODEC_FLAGS_FIELD1_ONLY 0x04 //!< Indicates missing fields +#define CODEC_FLAGS_FIELD2_ONLY 0x08 +#define CODEC_FLAGS_DOMINANCE 0x10 + +#define CODEC_FLAGS_INTERLACED_MASK 0x1F //!< Unused bits must be zero + +// Useful macros for testing the interlaced flags + +#define INTERLACED(flags) (((flags) & CODEC_FLAGS_INTERLACED) != 0) +#define PROGRESSIVE(flags) (((flags) & CODEC_FLAGS_INTERLACED) == 0) +#define FIELD_ORDER_NTSC(flags) (((flags) & CODEC_FLAGS_FIELD1_FIRST) == 0) +#define FIELD_ORDER_PAL(flags) (((flags) & CODEC_FLAGS_FIELD1_FIRST) != 0) +#define FIELD_ONE_ONLY(flags) (((flags) & CODEC_FLAGS_FIELD1_ONLY) != 0) +#define FIELD_TWO_ONLY(flags) (((flags) & CODEC_FLAGS_FIELD2_ONLY) != 0) +#define FIELD_ONE_PRESENT(flags) (((flags) & CODEC_FLAGS_FIELD2_ONLY) == 0) +#define FIELD_TWO_PRESENT(flags) (((flags) & CODEC_FLAGS_FIELD1_ONLY) == 0) +#define FIELD_BOTH_PRESENT(flags) (((flags) & (CODEC_FLAGS_FIELD1_ONLY | CODEC_FLAGS_FIELD1_ONLY)) == 0) + +// Bits in the copy protection flags + +#define CODEC_FLAGS_PROTECTED 0x01 //!< Copy protection flags +#define CODEC_FLAGS_PROTECTION_MASK 0x01 //!< Unused bits must be zero + +/*! + @brief Check that the bitstream is aligned to a segment boundary + + This function definition duplicates the same function in common/src/syntax.c, + but that file includes definitions intended only for the encoder. + + @todo Remove duplicate function definitions. + */ +bool IsAlignedSegment(BITSTREAM *stream) +{ + return (stream->count == 0 || stream->count == bit_word_count); +} + +/*! + @brief Convert the tag to an optional tag + + An optional tag has a negative value. +*/ +TAGWORD OptionalTag(TAGWORD tag) +{ + return ((tag < 0) ? tag : neg(tag)); +} + +/*! + @brief Convert the tag to a required tag + + An optional tag has a negative value. +*/ +TAGWORD RequiredTag(TAGWORD tag) +{ + return ((tag >= 0) ? tag : neg(tag)); +} + + +/*! + @brief Check that the bitstream is aligned to a tag word boundary + + @todo Check the places in the code where this function is used to + determine whether the bitstream should actually be aligned to a + segment boundary. +*/ +bool IsAlignedTag(BITSTREAM *stream) +{ + return ((stream->count % BITSTREAM_TAG_SIZE) == 0); +} + +/*! + @brief Pack the vector of prescale values into a single word + + The wavelet transform uses a vector of prescale values indexed by the + wavelet level with the input image at level zero to specify the amount + of prescaling that should be performed on the input the wavelet transform. + + This routine packs the prescale values into a segment value that can be + written into the bitstream. +*/ +TAGWORD PackTransformPrescale(TRANSFORM *transform) +{ + TAGWORD packed_prescale = 0; + int i; + + // Encode the prescale values that are actually used + for (i = 0; i < MAX_WAVELET_COUNT; i++) + { + assert((transform->prescale[i] & ~0x03) == 0); + packed_prescale += transform->prescale[i] << (14 - i * 2); + } + + // The remaining prescale values with filled with zeros + + return packed_prescale; +} + diff --git a/source/lib/vc5_common/syntax.h b/source/lib/vc5_common/syntax.h new file mode 100755 index 0000000..699c72e --- /dev/null +++ b/source/lib/vc5_common/syntax.h @@ -0,0 +1,129 @@ +/*! @file syntax.h + * + * @brief Declaration of bitstream elements and functions that define the syntax + * of an encoded sample. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SYNTAX_H +#define SYNTAX_H + +#define CODEC_TAG_SIZE 16 //!< Size of a codec tag (in bits) +#define CODEC_TAG_MASK 0xFFFF //!< Mask for usable part of tag or value + +typedef uint32_t SEGMENT; //!< The bitstream is a sequence of segments + +typedef int16_t TAGWORD; //!< Bitstream tag or value + +//! Number of bits in a tag or value +static const BITCOUNT tagword_count = 16; + +//! Number of bits in a segment (tag value pair) +static const BITCOUNT segment_count = 32; + +typedef union tagvalue //!< Bitstream tag and value pair +{ + struct { // Fields are in the order for byte swapping + TAGWORD value; + TAGWORD tag; + } tuple; //!< Tag value pair as separate members + + uint32_t longword; //!< Tag value pair as a int32_t word + +} TAGVALUE; + +/*! + @brief Values corresponding to the special codewords + + Special codewords are inserted into an entropy coded band to + mark certain locations in the bitstream. For example, the end + of an encoded band is marked by the band end codeword. Special + codewords are recorded in the codebook as entries that have a + run length of zero. The value indicates the syntax element that + is represented by the codeword. +*/ +typedef enum _special_marker +{ + SPECIAL_MARKER_BAND_END = 1, + +} SPECIAL_MARKER; + + +// The encoded quality is inserted into the bitstream using two tag value pairs +#define ENCODED_QUALITY_LOW_SHIFT 0 //!< Shift for the low part of the quality +#define ENCODED_QUALITY_LOW_MASK 0xFFFF //!< Mask for the low part of the quality +#define ENCODED_QUALITY_HIGH_SHIFT 16 //!< Shift for the high part of the quality +#define ENCODED_QUALITY_HIGH_MASK 0xFFFF //!< Mask for the high part of the quality + +#ifdef __cplusplus +extern "C" { +#endif + +TAGWORD RequiredTag(TAGWORD tag); + +//TAGVALUE GetSegment(BITSTREAM *stream); + +//TAGWORD GetValue(BITSTREAM *stream, int tag); + +// Output a tagged value with double word alignment +CODEC_ERROR PutTagPair(BITSTREAM *stream, int tag, int value); + +// Output an optional tagged value +CODEC_ERROR PutTagPairOptional(BITSTREAM *stream, int tag, int value); + +// Output a tag that marks a place in the bitstream for debugging +CODEC_ERROR PutTagMarker(BITSTREAM *stream, uint32_t marker, int size); + +TAGWORD OptionalTag(TAGWORD tag); + +//bool IsTagOptional(TAGWORD tag); + +//bool IsTagRequired(TAGWORD tag); + +//bool IsValidSegment(BITSTREAM *stream, TAGVALUE segment, TAGWORD tag); + +//CODEC_ERROR AlignBitsTag(BITSTREAM *stream); + +bool IsLowPassHeaderMarker(int marker); +bool IsLowPassBandMarker(int marker); +bool IsHighPassBandMarker(int marker); + +bool IsAlignedTag(BITSTREAM *stream); + +bool IsAlignedSegment(BITSTREAM *stream); + +// Write an index block for the sample bands +CODEC_ERROR PutGroupIndex(BITSTREAM *stream, + void *index_table[], + int index_table_length, + size_t *channel_size_table_offset); + +TAGWORD PackTransformPrescale(TRANSFORM *transform); + +//TODO: Move other declarations for routines that write syntax elements here +struct _encoder ; + +CODEC_ERROR PutFrameStructureFlags(struct _encoder *encoder, BITSTREAM *stream); + +// Output a tag and marker before the lowpass coefficients for debugging +CODEC_ERROR PutVideoLowpassMarker(BITSTREAM *stream); + +#ifdef __cplusplus +} +#endif + +#endif // SYNTAX_H diff --git a/source/lib/vc5_common/table17.inc b/source/lib/vc5_common/table17.inc new file mode 100755 index 0000000..9af5c19 --- /dev/null +++ b/source/lib/vc5_common/table17.inc @@ -0,0 +1,284 @@ +/*! @file table17.inc + + Codebook automatically generated by Huffman program + Created: Wed May 11 2005 21:23:08 UTC + + Modified by hand to simplify for use with the reference decoder. + + Note that the codebook entries with a run length of zero are + special codewords that are used to mark significant locations + in the encoded bitstream. + + (c) 2013 Society of Motion Picture & Television Engineers LLC and Woodman Labs, Inc. + All rights reserved--use subject to compliance with end user license agreement. +*/ +RLVTABLE(264) table17 = +{ + 264, + { + {1, 0x00000000, 1, 0}, // m0 + {2, 0x00000002, 1, 1}, // m1 + {3, 0x00000007, 1, 2}, // m2 + {5, 0x00000019, 1, 3}, // m3 + {6, 0x00000030, 1, 4}, // m4 + {6, 0x00000036, 1, 5}, // m5 + {7, 0x0000006F, 1, 8}, // m8 + {7, 0x00000063, 1, 6}, // m6 + {7, 0x00000069, 12, 0}, // z12 + {7, 0x0000006B, 1, 7}, // m7 + {8, 0x000000D1, 20, 0}, // z20 + {8, 0x000000D4, 1, 9}, // m9 + {8, 0x000000DC, 1, 10}, // m10 + {9, 0x00000189, 1, 11}, // m11 + {9, 0x0000018A, 32, 0}, // z32 + {9, 0x000001A0, 1, 12}, // m12 + {9, 0x000001AB, 1, 13}, // m13 + {10, 0x00000377, 1, 18}, // m18 + {10, 0x00000310, 1, 14}, // m14 + {10, 0x00000316, 1, 15}, // m15 + {10, 0x00000343, 60, 0}, // z60 + {10, 0x00000354, 1, 16}, // m16 + {10, 0x00000375, 1, 17}, // m17 + {11, 0x00000623, 1, 19}, // m19 + {11, 0x00000684, 1, 20}, // m20 + {11, 0x00000685, 100, 0}, // z100 + {11, 0x000006AB, 1, 21}, // m21 + {11, 0x000006EC, 1, 22}, // m22 + {12, 0x00000DDB, 1, 29}, // m29 + {12, 0x00000C5C, 1, 24}, // m24 + {12, 0x00000C5E, 1, 25}, // m25 + {12, 0x00000C44, 1, 23}, // m23 + {12, 0x00000D55, 1, 26}, // m26 + {12, 0x00000DD1, 1, 27}, // m27 + {12, 0x00000DD3, 1, 28}, // m28 + {13, 0x00001BB5, 1, 35}, // m35 + {13, 0x0000188B, 1, 30}, // m30 + {13, 0x000018BB, 1, 31}, // m31 + {13, 0x000018BF, 180, 0}, // z180 + {13, 0x00001AA8, 1, 32}, // m32 + {13, 0x00001BA0, 1, 33}, // m33 + {13, 0x00001BA5, 320, 0}, // z320 + {13, 0x00001BA4, 1, 34}, // m34 + {14, 0x00003115, 1, 36}, // m36 + {14, 0x00003175, 1, 37}, // m37 + {14, 0x0000317D, 1, 38}, // m38 + {14, 0x00003553, 1, 39}, // m39 + {14, 0x00003768, 1, 40}, // m40 + {15, 0x00006E87, 1, 46}, // m46 + {15, 0x00006ED3, 1, 47}, // m47 + {15, 0x000062E8, 1, 42}, // m42 + {15, 0x000062F8, 1, 43}, // m43 + {15, 0x00006228, 1, 41}, // m41 + {15, 0x00006AA4, 1, 44}, // m44 + {15, 0x00006E85, 1, 45}, // m45 + {16, 0x0000C453, 1, 48}, // m48 + {16, 0x0000C5D3, 1, 49}, // m49 + {16, 0x0000C5F3, 1, 50}, // m50 + {16, 0x0000DDA4, 1, 53}, // m53 + {16, 0x0000DD08, 1, 51}, // m51 + {16, 0x0000DD0C, 1, 52}, // m52 + {17, 0x0001BB4B, 1, 61}, // m61 + {17, 0x0001BB4A, 1, 60}, // m60 + {17, 0x00018BA5, 1, 55}, // m55 + {17, 0x00018BE5, 1, 56}, // m56 + {17, 0x0001AA95, 1, 57}, // m57 + {17, 0x0001AA97, 1, 58}, // m58 + {17, 0x000188A4, 1, 54}, // m54 + {17, 0x0001BA13, 1, 59}, // m59 + {18, 0x00031748, 1, 62}, // m62 + {18, 0x000317C8, 1, 63}, // m63 + {18, 0x00035528, 1, 64}, // m64 + {18, 0x0003552C, 1, 65}, // m65 + {18, 0x00037424, 1, 66}, // m66 + {18, 0x00037434, 1, 67}, // m67 + {18, 0x00037436, 1, 68}, // m68 + {19, 0x00062294, 1, 69}, // m69 + {19, 0x00062E92, 1, 70}, // m70 + {19, 0x00062F92, 1, 71}, // m71 + {19, 0x0006AA52, 1, 72}, // m72 + {19, 0x0006AA5A, 1, 73}, // m73 + {19, 0x0006E86A, 1, 75}, // m75 + {19, 0x0006E86E, 1, 76}, // m76 + {19, 0x0006E84A, 1, 74}, // m74 + {20, 0x000C452A, 1, 77}, // m77 + {20, 0x000C5D27, 1, 78}, // m78 + {20, 0x000C5F26, 1, 79}, // m79 + {20, 0x000D54A6, 1, 80}, // m80 + {20, 0x000D54B6, 1, 81}, // m81 + {20, 0x000DD096, 1, 82}, // m82 + {20, 0x000DD0D6, 1, 83}, // m83 + {20, 0x000DD0DE, 1, 84}, // m84 + {21, 0x00188A56, 1, 85}, // m85 + {21, 0x0018BA4D, 1, 86}, // m86 + {21, 0x0018BE4E, 1, 87}, // m87 + {21, 0x0018BE4F, 1, 88}, // m88 + {21, 0x001AA96E, 1, 89}, // m89 + {21, 0x001BA12E, 1, 90}, // m90 + {21, 0x001BA12F, 1, 91}, // m91 + {21, 0x001BA1AF, 1, 92}, // m92 + {21, 0x001BA1BF, 1, 93}, // m93 + {22, 0x0037435D, 1, 99}, // m99 + {22, 0x0037437D, 1, 100}, // m100 + {22, 0x00317498, 1, 94}, // m94 + {22, 0x0035529C, 1, 95}, // m95 + {22, 0x0035529D, 1, 96}, // m96 + {22, 0x003552DE, 1, 97}, // m97 + {22, 0x003552DF, 1, 98}, // m98 + {23, 0x0062E933, 1, 102}, // m102 + {23, 0x0062295D, 1, 101}, // m101 + {23, 0x006AA53D, 1, 103}, // m103 + {23, 0x006AA53F, 1, 105}, // m105 + {23, 0x006AA53E, 1, 104}, // m104 + {23, 0x006E86B9, 1, 106}, // m106 + {23, 0x006E86F8, 1, 107}, // m107 + {24, 0x00D54A79, 1, 111}, // m111 + {24, 0x00C5D265, 1, 109}, // m109 + {24, 0x00C452B8, 1, 108}, // m108 + {24, 0x00DD0D71, 1, 113}, // m113 + {24, 0x00D54A78, 1, 110}, // m110 + {24, 0x00DD0D70, 1, 112}, // m112 + {24, 0x00DD0DF2, 1, 114}, // m114 + {24, 0x00DD0DF3, 1, 115}, // m115 + {25, 0x0188A5F6, 1, 225}, // m225 + {25, 0x0188A5F5, 1, 189}, // m189 + {25, 0x0188A5F4, 1, 188}, // m188 + {25, 0x0188A5F3, 1, 203}, // m203 + {25, 0x0188A5F2, 1, 202}, // m202 + {25, 0x0188A5F1, 1, 197}, // m197 + {25, 0x0188A5F0, 1, 207}, // m207 + {25, 0x0188A5EF, 1, 169}, // m169 + {25, 0x0188A5EE, 1, 223}, // m223 + {25, 0x0188A5ED, 1, 159}, // m159 + {25, 0x0188A5AA, 1, 235}, // m235 + {25, 0x0188A5E3, 1, 152}, // m152 + {25, 0x0188A5DF, 1, 192}, // m192 + {25, 0x0188A589, 1, 179}, // m179 + {25, 0x0188A5DD, 1, 201}, // m201 + {25, 0x0188A578, 1, 172}, // m172 + {25, 0x0188A5E0, 1, 149}, // m149 + {25, 0x0188A588, 1, 178}, // m178 + {25, 0x0188A5D6, 1, 120}, // m120 + {25, 0x0188A5DB, 1, 219}, // m219 + {25, 0x0188A5E1, 1, 150}, // m150 + {25, 0x0188A587, 1, 127}, // m127 + {25, 0x0188A59A, 1, 211}, // m211 + {25, 0x0188A5C4, 1, 125}, // m125 + {25, 0x0188A5EC, 1, 158}, // m158 + {25, 0x0188A586, 1, 247}, // m247 + {25, 0x0188A573, 1, 238}, // m238 + {25, 0x0188A59C, 1, 163}, // m163 + {25, 0x0188A5C8, 1, 228}, // m228 + {25, 0x0188A5FB, 1, 183}, // m183 + {25, 0x0188A5A1, 1, 217}, // m217 + {25, 0x0188A5EB, 1, 168}, // m168 + {25, 0x0188A5A8, 1, 122}, // m122 + {25, 0x0188A584, 1, 128}, // m128 + {25, 0x0188A5D2, 1, 249}, // m249 + {25, 0x0188A599, 1, 187}, // m187 + {25, 0x0188A598, 1, 186}, // m186 + {25, 0x0188A583, 1, 136}, // m136 + {25, 0x018BA4C9, 1, 181}, // m181 + {25, 0x0188A5D0, 1, 255}, // m255 + {25, 0x0188A594, 1, 230}, // m230 + {25, 0x0188A582, 1, 135}, // m135 + {25, 0x0188A5CB, 1, 233}, // m233 + {25, 0x0188A5D8, 1, 222}, // m222 + {25, 0x0188A5E7, 1, 145}, // m145 + {25, 0x0188A581, 1, 134}, // m134 + {25, 0x0188A5EA, 1, 167}, // m167 + {25, 0x0188A5A9, 1, 248}, // m248 + {25, 0x0188A5A6, 1, 209}, // m209 + {25, 0x0188A580, 1, 243}, // m243 + {25, 0x0188A5A0, 1, 216}, // m216 + {25, 0x0188A59D, 1, 164}, // m164 + {25, 0x0188A5C3, 1, 140}, // m140 + {25, 0x0188A57F, 1, 157}, // m157 + {25, 0x0188A5C0, 1, 239}, // m239 + {25, 0x0188A5DE, 1, 191}, // m191 + {25, 0x0188A5D4, 1, 251}, // m251 + {25, 0x0188A57E, 1, 156}, // m156 + {25, 0x0188A5C2, 1, 139}, // m139 + {25, 0x0188A592, 1, 242}, // m242 + {25, 0x0188A5CD, 1, 133}, // m133 + {25, 0x0188A57D, 1, 162}, // m162 + {25, 0x0188A5A3, 1, 213}, // m213 + {25, 0x0188A5E8, 1, 165}, // m165 + {25, 0x0188A5A2, 1, 212}, // m212 + {25, 0x0188A57C, 1, 227}, // m227 + {25, 0x0188A58E, 1, 198}, // m198 + {25, 0x0188A5B3, 1, 236}, // m236 + {25, 0x0188A5B2, 1, 234}, // m234 + {25, 0x0188A5B1, 1, 117}, // m117 + {25, 0x0188A5B0, 1, 215}, // m215 + {25, 0x0188A5AF, 1, 124}, // m124 + {25, 0x0188A5AE, 1, 123}, // m123 + {25, 0x0188A5AD, 1, 254}, // m254 + {25, 0x0188A5AC, 1, 253}, // m253 + {25, 0x0188A5AB, 1, 148}, // m148 + {25, 0x0188A5DA, 1, 218}, // m218 + {25, 0x0188A5E4, 1, 146}, // m146 + {25, 0x0188A5E5, 1, 147}, // m147 + {25, 0x0188A5D9, 1, 224}, // m224 + {25, 0x0188A5B5, 1, 143}, // m143 + {25, 0x0188A5BC, 1, 184}, // m184 + {25, 0x0188A5BD, 1, 185}, // m185 + {25, 0x0188A5E9, 1, 166}, // m166 + {25, 0x0188A5CC, 1, 132}, // m132 + {25, 0x0188A585, 1, 129}, // m129 + {25, 0x0188A5D3, 1, 250}, // m250 + {25, 0x0188A5E2, 1, 151}, // m151 + {25, 0x0188A595, 1, 119}, // m119 + {25, 0x0188A596, 1, 193}, // m193 + {25, 0x0188A5B8, 1, 176}, // m176 + {25, 0x0188A590, 1, 245}, // m245 + {25, 0x0188A5C9, 1, 229}, // m229 + {25, 0x0188A5A4, 1, 206}, // m206 + {25, 0x0188A5E6, 1, 144}, // m144 + {25, 0x0188A5A5, 1, 208}, // m208 + {25, 0x0188A5CE, 1, 137}, // m137 + {25, 0x0188A5BF, 1, 241}, // m241 + {25, 0x0188A572, 1, 237}, // m237 + {25, 0x0188A59B, 1, 190}, // m190 + {25, 0x0188A5BE, 1, 240}, // m240 + {25, 0x0188A5C7, 1, 131}, // m131 + {25, 0x0188A5CA, 1, 232}, // m232 + {25, 0x0188A5D5, 1, 252}, // m252 + {25, 0x0188A57B, 1, 171}, // m171 + {25, 0x0188A58D, 1, 205}, // m205 + {25, 0x0188A58C, 1, 204}, // m204 + {25, 0x0188A58B, 1, 118}, // m118 + {25, 0x0188A58A, 1, 214}, // m214 + {25, 0x018BA4C8, 1, 180}, // m180 + {25, 0x0188A5C5, 1, 126}, // m126 + {25, 0x0188A5FA, 1, 182}, // m182 + {25, 0x0188A5BB, 1, 175}, // m175 + {25, 0x0188A5C1, 1, 141}, // m141 + {25, 0x0188A5CF, 1, 138}, // m138 + {25, 0x0188A5B9, 1, 177}, // m177 + {25, 0x0188A5B6, 1, 153}, // m153 + {25, 0x0188A597, 1, 194}, // m194 + {25, 0x0188A5FE, 1, 160}, // m160 + {25, 0x0188A5D7, 1, 121}, // m121 + {25, 0x0188A5BA, 1, 174}, // m174 + {25, 0x0188A591, 1, 246}, // m246 + {25, 0x0188A5C6, 1, 130}, // m130 + {25, 0x0188A5DC, 1, 200}, // m200 + {25, 0x0188A57A, 1, 170}, // m170 + {25, 0x0188A59F, 1, 221}, // m221 + {25, 0x0188A5F9, 1, 196}, // m196 + {25, 0x0188A5B4, 1, 142}, // m142 + {25, 0x0188A5A7, 1, 210}, // m210 + {25, 0x0188A58F, 1, 199}, // m199 + {25, 0x0188A5FD, 1, 155}, // m155 + {25, 0x0188A5B7, 1, 154}, // m154 + {25, 0x0188A593, 1, 244}, // m244 + {25, 0x0188A59E, 1, 220}, // m220 + {25, 0x0188A5F8, 1, 195}, // m195 + {25, 0x0188A5FF, 1, 161}, // m161 + {25, 0x0188A5FC, 1, 231}, // m231 + {25, 0x0188A579, 1, 173}, // m173 + {25, 0x0188A5F7, 1, 226}, // m226 + {26, 0x03114BA2, 1, 116}, // m116 + {26, 0x03114BA3, 0, 1}, // c256 + } +}; diff --git a/source/lib/vc5_common/types.h b/source/lib/vc5_common/types.h new file mode 100755 index 0000000..2e5f5b9 --- /dev/null +++ b/source/lib/vc5_common/types.h @@ -0,0 +1,94 @@ +/*! @file types.h + * + * @brief Definition of some common data types used by the codec. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef TYPES_H +#define TYPES_H + +//! Data type for Boolean variables +typedef uint_fast8_t BOOLEAN; + +//! Data type for image and frame dimensions +typedef uint_least16_t DIMENSION; + +//! Data type for an unsigned number (count) that can be the value in a tag-value pair +typedef uint_least16_t COUNT; + +/*! + @brief Define a data type large enough to hold a quantization value + + Define a data type that is large enough to hold any quantization value. + Older code assumed that the quantization data type was an int, but newer + could should use the QUANT data type. After all quantization values are + stored in a QUANT data type, the data type can be redefined as a byte. +*/ +typedef int QUANT; + +/*! + @brief Integer value for the amount of prescale shift + + @todo Redefine this data type to be uint_fast8_t? +*/ +typedef uint16_t PRESCALE; + + +/*! + @brief Number of bits in a component value +*/ +typedef uint_least8_t PRECISION; + +enum +{ + PRECISION_MIN = 8, + PRECISION_MAX = 32, +}; + +/*! + @brief Data type for the channel number +*/ +typedef uint_least8_t CHANNEL; + +/*! + @brief Data type for the bit mask that represents enabled parts + + The bit mask indicates which parts of the VC-5 standard are enabled + at runtime. +*/ +typedef uint32_t ENABLED_PARTS; + +/*! + @brief Codec version number (major, minor, revision, build) + + The major and minor product numbers identify versions that are released. + Revision numbers are used to identify interim releases for bug fixes. + The build number is incremented for every build, independent of product + numbering, to uniquely identify every build that is a release candidate. +*/ +typedef struct _version +{ + uint8_t major; //!< Major product number + uint8_t minor; //!< Minor product number + uint8_t revision; //!< Product revision + uint8_t padding; //!< Padding to a multiple of 4 bytes + uint32_t build; //!< Unique number for each codec build +} VERSION; + +#define VERSION_INITIALIZER(major, minor, revision, build) {major, minor, revision, 0, build} + +#endif // TYPES_H diff --git a/source/lib/vc5_common/unique.h b/source/lib/vc5_common/unique.h new file mode 100755 index 0000000..b90942e --- /dev/null +++ b/source/lib/vc5_common/unique.h @@ -0,0 +1,42 @@ +/*! @file unique.h + * + * @brief Declaration of data structures for the unique image identifier. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UNIQUE_H +#define UNIQUE_H + +// Basic UMID label using the UUID method + +static const uint8_t UMID_label[] = { + 0x06, 0x0A, 0x2B, 0x34, 0x01, 0x01, 0x01, 0x05, 0x01, 0x01, 0x01, 0x20, +}; + +// Length of the UMID (in bytes) +#define UMID_size 32 + +// Length of the unique identifier chunk payload (in segments) +#define UMID_length (UMID_size/sizeof(SEGMENT)) + +// Length of the image sequence number (in bytes) +#define sequence_number_size sizeof(uint32_t) + +// Length of the image sequence number (in segments) +#define sequence_number_length (sequence_number_size/sizeof(SEGMENT)) + +#endif // UNIQUE_H diff --git a/source/lib/vc5_common/utilities.c b/source/lib/vc5_common/utilities.c new file mode 100755 index 0000000..adc8678 --- /dev/null +++ b/source/lib/vc5_common/utilities.c @@ -0,0 +1,82 @@ +/*! @file utilities.c + * + * @brief The utilities in this file are included to allow the codec to be tested. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +/*! + @brief Check that the enabled parts are correct +*/ +CODEC_ERROR CheckEnabledParts(ENABLED_PARTS *enabled_parts_ref) +{ + ENABLED_PARTS enabled_parts = (*enabled_parts_ref); + + // The elementary bitstream is always enabled + if ((enabled_parts & VC5_PART_MASK(VC5_PART_ELEMENTARY)) == 0) { + enabled_parts |= VC5_PART_MASK(VC5_PART_ELEMENTARY); + } + + // The conformance specification is never enabled + enabled_parts &= ~((uint32_t)VC5_PART_MASK(VC5_PART_CONFORMANCE)); + + // Image formats must be enabled if subsampled color differences are enabled + if ((enabled_parts & VC5_PART_MASK(VC5_PART_COLOR_SAMPLING)) != 0) { + enabled_parts |= VC5_PART_MASK(VC5_PART_IMAGE_FORMATS); + } + + // Check that the enabled parts were built at compile-time + //assert((enabled_parts & VC5_ENABLED_PARTS) == enabled_parts); + if (! ((enabled_parts & VC5_ENABLED_PARTS) == enabled_parts)) { + return CODEC_ERROR_ENABLED_PARTS; + } + + // Return the correct enabled parts mask + *enabled_parts_ref = enabled_parts; + return CODEC_ERROR_OKAY; +} + +/*! + @brief Verify that the enabled parts are correct +*/ +CODEC_ERROR VerifyEnabledParts(ENABLED_PARTS enabled_parts) +{ + // The elementary bitstream must always be enabled + if ((enabled_parts & VC5_PART_MASK(VC5_PART_ELEMENTARY)) == 0) { + return CODEC_ERROR_ENABLED_PARTS; + } + + // The conformance specification must not be enabled + if ((enabled_parts & VC5_PART_MASK(VC5_PART_CONFORMANCE)) != 0) { + return CODEC_ERROR_ENABLED_PARTS; + } + + // Image formats must be enabled if subsampled color differences are enabled + if ((enabled_parts & VC5_PART_MASK(VC5_PART_COLOR_SAMPLING)) != 0 && + (enabled_parts & VC5_PART_MASK(VC5_PART_IMAGE_FORMATS)) == 0) { + return CODEC_ERROR_ENABLED_PARTS; + } + + // All enabled parts must be compiled into this codec implementation + if ((enabled_parts & VC5_ENABLED_PARTS) != enabled_parts) { + return CODEC_ERROR_ENABLED_PARTS; + } + + // This codec implementation supports the enabled parts of the VC-5 standard + return CODEC_ERROR_OKAY; +} diff --git a/source/lib/vc5_common/utilities.h b/source/lib/vc5_common/utilities.h new file mode 100755 index 0000000..851515b --- /dev/null +++ b/source/lib/vc5_common/utilities.h @@ -0,0 +1,36 @@ +/*! @file utilities.c + * + * @brief Utility routines used by the code for testing the codec. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef UTILITIES_H +#define UTILITIES_H + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR CheckEnabledParts(ENABLED_PARTS *enabled_parts_ref); + + CODEC_ERROR VerifyEnabledParts(ENABLED_PARTS enabled_parts); + +#ifdef __cplusplus +} +#endif + +#endif // UTILITIES_H diff --git a/source/lib/vc5_common/vc5_common.h b/source/lib/vc5_common/vc5_common.h new file mode 100644 index 0000000..a83dc41 --- /dev/null +++ b/source/lib/vc5_common/vc5_common.h @@ -0,0 +1,34 @@ +/*! @file vc5_common.h + * + * @brief Declaration of items that are used by encoder and decoder API + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VC5_COMMON_H +#define VC5_COMMON_H + +#include "config.h" + +// ================================================================================================= +// VC5 version numbering +// ================================================================================================= + +#define VC5_VERSION_MAJOR 1 +#define VC5_VERSION_MINOR 0 +#define VC5_VERSION_REVISION 0 + +#endif // VC5_COMMON_H diff --git a/source/lib/vc5_common/wavelet.c b/source/lib/vc5_common/wavelet.c new file mode 100755 index 0000000..ec7e31a --- /dev/null +++ b/source/lib/vc5_common/wavelet.c @@ -0,0 +1,519 @@ +/*! @file wavelet.c + * + * @brief Implementation of the module for wavelet data structures and transforms + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "common.h" + +/*! + @brief Table of prescale values for the spatial wavelet transform + + Each prescale value is indexed by the wavelet level. Index level zero + corresponds to the input frame, the other prescale values correspond to + wavelets in the transform: frame, spatial, spatial, ... + + Note that the prescale values depend on the encoded precision. The default + precale values are for 10-bit precision and the actual will depend on the + encoded precision. +*/ +const int spatial_prescale[] = {0, 2, 0, 0, 0, 0, 0, 0}; + +/*! + @brief Initialize a wavelet data structure with the specified dimensions +*/ +CODEC_ERROR InitWavelet(WAVELET *wavelet, DIMENSION width, DIMENSION height) +{ + assert(wavelet != NULL); + if (! (wavelet != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + memset(wavelet, 0, sizeof(WAVELET)); + + wavelet->width = width; + wavelet->height = height; + wavelet->band_count = 4; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Allocate a wavelet data structure with the specified dimensions +*/ +CODEC_ERROR AllocWavelet(gpr_allocator *allocator, WAVELET *wavelet, DIMENSION width, DIMENSION height) +{ + // Initialize the fields in the wavelet data structure + InitWavelet(wavelet, width, height); + + if (width > 0 && height > 0) + { + int band; + + //TODO: Align the pitch? + DIMENSION pitch = width * sizeof(PIXEL); + + size_t band_data_size = height * pitch; + + PIXEL* data_all_bands = (PIXEL *)allocator->Alloc(band_data_size * MAX_BAND_COUNT); + + if ( data_all_bands == NULL ) + { + ReleaseWavelet(allocator, wavelet); + return CODEC_ERROR_OUTOFMEMORY; + } + + // Allocate the wavelet bands + for (band = 0; band < MAX_BAND_COUNT; band++) + { + wavelet->data[band] = data_all_bands + band * height * width; + } + + wavelet->pitch = pitch; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Release all resources allocated to the wavelet + + The wavelet data structure itself is not reallocated. +*/ +CODEC_ERROR ReleaseWavelet(gpr_allocator *allocator, WAVELET *wavelet) +{ + int band; + + PIXEL* data_all_bands = wavelet->data[0]; + + allocator->Free(data_all_bands); + + for (band = 0; band < MAX_BAND_COUNT; band++) { + wavelet->data[band] = NULL; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Create and allocate a wavelet data structure +*/ +WAVELET *CreateWavelet(gpr_allocator *allocator, DIMENSION width, DIMENSION height) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + if (width > 0 && height > 0) + { + // Allocate the image data structure for the wavelet + WAVELET *wavelet = (WAVELET *)allocator->Alloc(sizeof(WAVELET)); + assert(wavelet != NULL); + if (! (wavelet != NULL)) { + return NULL; + } + + // Allocate space for the wavelet bands + error = AllocWavelet(allocator, wavelet, width, height); + if (error == CODEC_ERROR_OKAY) { + return wavelet; + } + + // Avoid a memory leak + DeleteWavelet(allocator, wavelet); + } + + return NULL; +} + +/*! + @brief Release all resources the free the wavelet data structure +*/ +CODEC_ERROR DeleteWavelet(gpr_allocator *allocator, WAVELET *wavelet) +{ + ReleaseWavelet(allocator, wavelet); + allocator->Free(wavelet); + return CODEC_ERROR_OKAY; +} + +/*! + @brief Compute the amount of scaling for each band in the wavelet tree + + The forward wavelet transforms increase the number of bits required to + represent the coefficients. This routine computes the amount by which + the input pixels are scaled as each band is computed. The horizontal or + vertical transform scales the lowpass values by one bit, but the hgihpass + values are not scaled. The lowpass wavelet band will be scaled by two + bits, the first and second highpass bands will be scaled by one bit, and + the third highpass band will not be scaled. +*/ +CODEC_ERROR SetTransformScale(TRANSFORM *transform) +{ + //int num_wavelets = 3; + int num_spatial = 2; + + int num_lowpass_spatial; // Number of spatial transforms for lowpass temporal band + //int num_highpass_spatial; // Number of spatial transforms for highpass temporal band + int num_frame_wavelets; // Number of frame wavelets at the base of the pyramid + + // Area of the temporal lowpass filter + int temporal_lowpass_area = 2; + + // Area of the each wavelet filter + int horizontal_lowpass_area = 2; + int vertical_lowpass_area = 2; + + // Combination of the horizontal and vertical wavelet transforms + int spatial_lowpass_area = (horizontal_lowpass_area * vertical_lowpass_area); + + int temporal_lowpass_scale; + int temporal_highpass_scale; + + WAVELET *wavelet = NULL; + //WAVELET *temporal; + + int k; + int i; + + // Coefficients in each band are scaled by the forward wavelet filters + int scale[4] = {1, 1, 1, 1}; + + // Compute the number of frame and spatial wavelets + num_frame_wavelets = 1; + num_lowpass_spatial = num_spatial; + + // Compute the change in scale due to the filters used in the frame transform + temporal_lowpass_scale = temporal_lowpass_area * scale[0]; + temporal_highpass_scale = scale[0]; + + // Compute the scale factors for the first wavelet + scale[0] = horizontal_lowpass_area * temporal_lowpass_scale; + scale[1] = temporal_lowpass_scale; + scale[2] = horizontal_lowpass_area * temporal_highpass_scale; + scale[3] = temporal_highpass_scale; + + for (k = 0; k < num_frame_wavelets; k++) + { + wavelet = transform->wavelet[k]; + assert(wavelet != NULL); + if (! (wavelet != NULL)) { + return CODEC_ERROR_UNEXPECTED; + } + + wavelet->scale[0] = scale[0]; + wavelet->scale[1] = scale[1]; + wavelet->scale[2] = scale[2]; + wavelet->scale[3] = scale[3]; + } + + // Compute the scale factors for the spatial wavelets + for (i = 0; i < num_lowpass_spatial; i++) + { + WAVELET *spatial = transform->wavelet[k++]; + //int k; + + assert(spatial != NULL); + if (! (spatial != NULL)) { + return CODEC_ERROR_UNEXPECTED; + } + + assert(wavelet != NULL); + if (! (wavelet != NULL)) { + return CODEC_ERROR_UNEXPECTED; + } + + // The lowpass band is the input to the spatial transform + temporal_lowpass_scale = wavelet->scale[0]; + + spatial->scale[0] = (spatial_lowpass_area * temporal_lowpass_scale);// >> _LOWPASS_PRESCALE; + spatial->scale[1] = (vertical_lowpass_area * temporal_lowpass_scale);// >> _LOWPASS_PRESCALE; + spatial->scale[2] = (horizontal_lowpass_area * temporal_lowpass_scale);// >> _LOWPASS_PRESCALE; + spatial->scale[3] = (temporal_lowpass_scale);// >> _LOWPASS_PRESCALE; + + // The spatial wavelet is the input for the next level + wavelet = spatial; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Calculate the amount of prescaling required to prevent overflow + + This routine calculates the arithmetic right shift that is applied to each + lowpass band prior to computation of the wavelet transform. Prescaling is + required to prevent overflow in the wavelet transforms. +*/ +CODEC_ERROR SetTransformPrescale(TRANSFORM *transform, int precision) +{ + if (precision == 8) + { + memset(transform->prescale, 0, sizeof(transform->prescale)); + return CODEC_ERROR_OKAY; + } + else if (precision == 10) + { + PRESCALE spatial_prescale[] = {0, 2, 2, 0, 0, 0, 0, 0}; + memcpy(transform->prescale, spatial_prescale, sizeof(transform->prescale)); + } + else if (precision == 12) + { + // frame, spatial, spatial, ... + PRESCALE spatial_prescale[] = {0, 2, 2, 0, 0, 0, 0, 0}; + memcpy(transform->prescale, spatial_prescale, sizeof(transform->prescale)); + } + else + { + //TODO: Need to handle other precisions + assert(0); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Return a mask for the specified wavelet band + + The wavelet data structure contains a mask that indicates which + bands have been decoded. +*/ +bool BandValidMask(int band) +{ + return (1 << band); +} + +/*! + @brief Check that all bands are valid + + The wavelet valid band mask is checked to determine whether + all of the bands in the wavelet have been decoded. +*/ +bool BandsAllValid(WAVELET *wavelet) +{ + uint32_t all_bands_valid_mask = ((1 << wavelet->band_count) - 1); + return (wavelet->valid_band_mask == all_bands_valid_mask); +} + +/*! + @brief Set the bit for the specified band in the valid band mask +*/ +CODEC_ERROR UpdateWaveletValidBandMask(WAVELET *wavelet, int band) +{ + if (0 <= band && band < MAX_BAND_COUNT) + { + // Update the valid wavelet band flags + wavelet->valid_band_mask |= (1 << band); + return CODEC_ERROR_OKAY; + } + return CODEC_ERROR_INVALID_BAND; +} + +/*! + @brief Compute the wavelet index from the subband index + + All subbands that are encoded into the bitstream, including the + lowpass band at the highest wavelet level, are numbered in decode + order starting with zero for the lowpass band. + + This routine maps the subband index to the index of the wavelet + that contains the specified subband. + + Note the sifference between a wavelet band and a subband: The bands in + each wavelet are numbered starting at zero, while the subband index + applies to all wavelet bands in the encoded sample and does not include + the lowpass bands that are reconstructed during decoding from the bands + that were encoded into the bitstream. +*/ +int SubbandWaveletIndex(int subband) +{ + //TODO: Adjust for other transform types and decoded resolutions + static int subband_wavelet_index[] = {2, 2, 2, 2, 1, 1, 1, 0, 0, 0}; + + assert(0 <= subband && subband < MAX_SUBBAND_COUNT); + + // Return the index of the wavelet corresponding to this subband + return subband_wavelet_index[subband]; +} + +/*! + @brief Compute the index for the band in a wavelet from the subband index + + See the explanation of wavelet bands and subbands in the documentation for + @ref SubbandWaveletIndex. +*/ +int SubbandBandIndex(int subband) +{ + //TODO: Adjust for other transform types and decoded resolutions + static int subband_band_index[] = {0, 1, 2, 3, 1, 2, 3, 1, 2, 3}; + + assert(0 <= subband && subband < MAX_SUBBAND_COUNT); + + // Return the index to the band within the wavelet + return subband_band_index[subband]; +} + +/*! + @brief Free the wavelets allocated for this transform +*/ +CODEC_ERROR ReleaseTransform(gpr_allocator *allocator, TRANSFORM *transform) +{ + int wavelet_index; + + for (wavelet_index = 0; wavelet_index < MAX_WAVELET_COUNT; wavelet_index++) + { + WAVELET *wavelet = transform->wavelet[wavelet_index]; + if (wavelet != NULL) { + DeleteWavelet(allocator, wavelet); + transform->wavelet[wavelet_index] = NULL; + } + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Return true if the prescale table is the same as the default table + + This routine compares the prescale values used by the transform with the default + table of prescale values. If the actual prescale values are the same as the + default values, then the table of prescale values do not have to be encoded + into the bitstream. +*/ +bool IsTransformPrescaleDefault(TRANSFORM *transform, int precision) +{ + int prescale_count = sizeof(transform->prescale) / sizeof(transform->prescale[0]); + int total = 0; + int i; + + if (precision == 8) + { + for (i = 0; i < prescale_count; i++) { + total += transform->prescale[i]; + } + return (total == 0); + } + + for (i = 0; i < prescale_count; i++) { + total += absolute(transform->prescale[i] - spatial_prescale[i]); + } + for(; i < MAX_PRESCALE_COUNT; i++) { + total += spatial_prescale[i]; + } + return (total == 0); +} + +PIXEL *WaveletRowAddress(WAVELET *wavelet, int band, int row) +{ + assert(wavelet != NULL); + if (! (wavelet != NULL)) { + return NULL; + } + + assert(0 <= row && row < wavelet->height); + if (! (0 <= row && row < wavelet->height)) + { + return NULL; + } + else + { + uint8_t *address = (uint8_t *)wavelet->data[band]; + address += row * wavelet->pitch; + return (PIXEL *)address; + } +} + +void WaveletToRGB( gpr_allocator allocator, PIXEL* GS_src, PIXEL* RG_src, PIXEL* BG_src, DIMENSION src_width, DIMENSION src_height, DIMENSION src_pitch, RGB_IMAGE *dst_image, + int input_precision_bits, int output_precision_bits, gpr_rgb_gain* rgb_gain ) +{ + TIMESTAMP("[BEG]", 2) + + assert( dst_image ); + assert( dst_image->buffer == NULL ); + + size_t size; + if( output_precision_bits == 8 ) + { + size = src_width * src_height * 3; + } + else + { + size = src_width * src_height * 6; + } + + dst_image->width = src_width; + dst_image->height = src_height; + dst_image->pitch = src_width * 3; + dst_image->size = size; + dst_image->buffer = allocator.Alloc( size ); + + const int32_t midpoint = (1 << (input_precision_bits - 1)); + const int32_t shift = input_precision_bits - 12; + + unsigned char* RGB_dst_8bits = dst_image->buffer; + unsigned short* RGB_dst_16bits = dst_image->buffer; + + DIMENSION x, y; + + for ( y = 0; y < src_height; y++) + { + for ( x = 0; x < src_width; x++) + { + int32_t G = GS_src[ (src_width - x - 1) + y * src_pitch]; + int32_t R = 2 * ( RG_src[(src_width - x - 1) + y * src_pitch] - midpoint) + G; + int32_t B = 2 * ( BG_src[(src_width - x - 1) + y * src_pitch] - midpoint) + G; + + // R,G,B are in 16-bit range since DecoderLogCurve outputs in 16 bits (although it's input is 12 bits) + R = DecoderLogCurve[ clamp_uint( (R >> shift), 12) ]; + G = DecoderLogCurve[ clamp_uint( (G >> shift), 12) ]; + B = DecoderLogCurve[ clamp_uint( (B >> shift), 12) ]; + + if( output_precision_bits == 8 ) + { + R *= rgb_gain->r_gain_num; + R >>= rgb_gain->r_gain_pow2_den; + + G *= rgb_gain->g_gain_num; + G >>= rgb_gain->g_gain_pow2_den; + + B *= rgb_gain->b_gain_num; + B >>= rgb_gain->b_gain_pow2_den; + + R = sqrtf((float)R); + G = sqrtf((float)G); + B = sqrtf((float)B); + + R = clamp_uint8( R ); + G = clamp_uint8( G ); + B = clamp_uint8( B ); + + RGB_dst_8bits[3 * (x) + 0 + y * dst_image->pitch] = R; + RGB_dst_8bits[3 * (x) + 1 + y * dst_image->pitch] = G; + RGB_dst_8bits[3 * (x) + 2 + y * dst_image->pitch] = B; + } + else + { + R = clamp_uint16( R ); + G = clamp_uint16( G ); + B = clamp_uint16( B ); + + RGB_dst_16bits[3 * (x) + 0 + y * dst_image->pitch] = ( (R & 0x00FF) << 8 ) | ( (R & 0xFF00) >> 8 ); + RGB_dst_16bits[3 * (x) + 1 + y * dst_image->pitch] = ( (G & 0x00FF) << 8 ) | ( (G & 0xFF00) >> 8 ); + RGB_dst_16bits[3 * (x) + 2 + y * dst_image->pitch] = ( (B & 0x00FF) << 8 ) | ( (B & 0xFF00) >> 8 ); + } + } + } + + TIMESTAMP("[BEG]", 2) +} diff --git a/source/lib/vc5_common/wavelet.h b/source/lib/vc5_common/wavelet.h new file mode 100755 index 0000000..cc5e2b6 --- /dev/null +++ b/source/lib/vc5_common/wavelet.h @@ -0,0 +1,126 @@ +/*! @file vlc.h + * + * @brief This file defines the data structures for the wavelet tree + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef WAVELET_H +#define WAVELET_H + +#include "common.h" + +/*! + @brief Data structure used for wavelets + + This data structure is used for wavelets and can be used for images since + an image with multiple planar channels and a wavelet with multiple bands + are similar data structures. + + The pitch is the distance between rows in bytes and must always be + an integer multiple of the pixel size in bytes. + + The wavelet data structure contains an array of the scale factor for + each band that is the cummulative result of the application of the + wavelet transforms that created the wavelet. +*/ +typedef struct _wavelet +{ + DIMENSION width; //!< Width of the image in pixels + DIMENSION height; //!< Height of the image in lines + DIMENSION pitch; //!< Distance between rows (in bytes) + uint16_t band_count; //!< Number of bands in a wavelet + uint32_t valid_band_mask; //!< Mask indicating which bands have been decoded + uint16_t scale[MAX_BAND_COUNT]; //!< Cumulative scaling by the wavelet transforms + QUANT quant[MAX_BAND_COUNT]; //!< Quantization value for each band + PIXEL *data[MAX_BAND_COUNT]; //!< Data buffer for each band + +} WAVELET; + +//! Indices for the wavelet bands in the image data structure +typedef enum +{ + LL_BAND = 0, //!< Lowpass transform of lowpass intermediate result + LH_BAND, //!< Lowpass transform of highpass intermediate result + HL_BAND, //!< Highpass transform of lowpass intermediate result + HH_BAND //!< Highpass transform of highpass intermediate result +} WAVELET_BAND; + +//! Types of wavelet tranforms +enum +{ + WAVELET_TYPE_HORIZONTAL = 1, + WAVELET_TYPE_VERTICAL = 2, + WAVELET_TYPE_TEMPORAL = 4, + + //! The baseline profile only supports spatial wavelets + WAVELET_TYPE_SPATIAL = (WAVELET_TYPE_HORIZONTAL | WAVELET_TYPE_VERTICAL), + +}; + +//! Data structure for the wavelet tree (one channel) +typedef struct _transform +{ + //! Prescale the input by the specified shift before the transform + PRESCALE prescale[MAX_WAVELET_COUNT]; + + //! List of the wavelets in the transform for one channel + WAVELET *wavelet[MAX_WAVELET_COUNT]; + +} TRANSFORM; + + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR AllocWavelet(gpr_allocator *allocator, WAVELET *wavelet, DIMENSION width, DIMENSION height); + CODEC_ERROR ReleaseWavelet(gpr_allocator *allocator, WAVELET *wavelet); + + WAVELET *CreateWavelet(gpr_allocator *allocator, DIMENSION width, DIMENSION height); + CODEC_ERROR DeleteWavelet(gpr_allocator *allocator, WAVELET *wavelet); + + CODEC_ERROR SetTransformScale(TRANSFORM *transform); + + CODEC_ERROR SetTransformPrescale(TRANSFORM *transform, int precision); + + bool BandValidMask(int band); + + bool BandsAllValid(WAVELET *wavelet); + #define AllBandsValid BandsAllValid + + CODEC_ERROR UpdateWaveletValidBandMask(WAVELET *wavelet, int band); + + int SubbandWaveletIndex(int subband); + + int SubbandBandIndex(int subband); + + CODEC_ERROR ResetTransformFlags(TRANSFORM transform[], int transform_count); + + CODEC_ERROR ReleaseTransform(gpr_allocator *allocator, TRANSFORM *transform); + + bool IsTransformPrescaleDefault(TRANSFORM *transform, int precision); + + PIXEL *WaveletRowAddress(WAVELET *wavelet, int band, int row); + + void WaveletToRGB( gpr_allocator allocator, PIXEL* GS_src, PIXEL* RG_src, PIXEL* BG_src, DIMENSION src_width, DIMENSION src_height, DIMENSION src_pitch, RGB_IMAGE *dst_image, + int input_precision_bits, int output_precision_bits, gpr_rgb_gain* rgb_gain ); + +#ifdef __cplusplus +} +#endif + +#endif // WAVELET_H diff --git a/source/lib/vc5_decoder/CMakeLists.txt b/source/lib/vc5_decoder/CMakeLists.txt new file mode 100644 index 0000000..d78522d --- /dev/null +++ b/source/lib/vc5_decoder/CMakeLists.txt @@ -0,0 +1,22 @@ +# library +set( LIB_NAME vc5_decoder ) + +# get source files +file( GLOB SRC_FILES "*.c" ) + +# get include files +file( GLOB INC_FILES "*.h" ) + +# add include files from other folders +include_directories( "../vc5_common" ) +include_directories( "../common/private" ) +include_directories( "../common/public" ) + + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/source/lib/vc5_decoder/codebooks.c b/source/lib/vc5_decoder/codebooks.c new file mode 100755 index 0000000..cfd40d4 --- /dev/null +++ b/source/lib/vc5_decoder/codebooks.c @@ -0,0 +1,36 @@ +/*! @file codebooks.c + * + * @brief Implementation of routines for the inverse component transform and permutation. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" +#include "table17.inc" + +/*! + @brief Define the codeset used by the reference codec + + The baseline codec only supports codebook #17. + + Codebook #17 is intended to be used with cubic companding + (see @ref FillMagnitudeEncodingTable and @ref ComputeCubicTable). + */ +DECODER_CODESET decoder_codeset_17 = { + "Codebook set 17 from data by David Newman with tables automatically generated for the FSM decoder", + (const CODEBOOK *)&table17, + CODESET_FLAGS_COMPANDING_CUBIC, +}; diff --git a/source/lib/vc5_decoder/codebooks.h b/source/lib/vc5_decoder/codebooks.h new file mode 100755 index 0000000..94670ee --- /dev/null +++ b/source/lib/vc5_decoder/codebooks.h @@ -0,0 +1,50 @@ +/*! @file codebooks.h + * + * @brief Declaration of routines for the inverse component transform and permutation. + * The collection of codebooks that are used by the decoder are called a codeset. + * The codebook in seach codeset is derived from the master codebook that is + * included in the codec by including the table for the codebook. The encoder + * uses specialized codebooks for coefficient magnitudes and runs of zeros that + * are derived from the master codebook. + + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODEBOOKS_H +#define CODEBOOKS_H + +#ifdef __cplusplus +extern "C" { +#endif + + typedef struct decoder_codeset { + + const char *title; //!< Identifying string for the codeset + + const CODEBOOK *codebook; //!< Codebook for runs and magnitudes + + uint32_t flags; //!< Encoding flags (see the codeset flags) + + } DECODER_CODESET; + + //TODO: Need to support other codesets in the reference decoder? + extern DECODER_CODESET decoder_codeset_17; + +#ifdef __cplusplus +} +#endif + +#endif // CODEBOOKS_H diff --git a/source/lib/vc5_decoder/component.c b/source/lib/vc5_decoder/component.c new file mode 100755 index 0000000..c224dc5 --- /dev/null +++ b/source/lib/vc5_decoder/component.c @@ -0,0 +1,111 @@ +/*! @file component.c + * + * @brief Code for parsing the inverse component transform and inverse component permutation. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +CODEC_ERROR ParseInverseComponentTransform(DECODER *decoder, BITSTREAM *stream, size_t chunk_size) +{ + //CODEC_ERROR error = CODEC_ERROR_OKAY; + CODEC_STATE *codec = &decoder->codec; + int component_count = codec->channel_count; + int padding; + int i; + +#if VC5_ENABLED_PART(VC5_PART_COLOR_SAMPLING) + if (IsPartEnabled(decoder->enabled_parts, VC5_PART_COLOR_SAMPLING)) + { + // Recompute the number of components to account for color difference component subsampling + component_count = codec->pattern_width * codec->pattern_height + 2; + } +#endif + + // Compute the padding (in bytes) from the end of the component transform to the end of the chunk payload + padding = (int)((chunk_size * sizeof(SEGMENT)) - ((component_count + 2) * component_count) * sizeof(uint8_t)); + + for (i = 0; i < component_count; i++) + { + int offset; + int scale; + int j; + + for (j = 0; j < component_count; j++) + { + int matrix_index = i * component_count + j; + int matrix_value = GetBits(stream, 8); + + //TODO: Need to save the value in the codec state + (void)matrix_index; + (void)matrix_value; + } + + offset = GetBits(stream, 8); + scale = GetBits(stream, 8); + + //TODO: Need to save the offset and scale in the codec state + (void)offset; + (void)scale; + } + + // Skip the padding at the end of the chunk payload + GetBits(stream, 8 * padding); + + // Should be at the end of the last segment in the chunk + assert(IsAlignedSegment(stream)); + + return CODEC_ERROR_OKAY; +} + +CODEC_ERROR ParseInverseComponentPermutation(DECODER *decoder, BITSTREAM *stream, size_t chunk_size) +{ + //CODEC_ERROR error = CODEC_ERROR_OKAY; + CODEC_STATE *codec = &decoder->codec; + int component_count = codec->channel_count; + int padding; + int i; + +#if VC5_ENABLED_PART(VC5_PART_COLOR_SAMPLING) + if (IsPartEnabled(decoder->enabled_parts, VC5_PART_COLOR_SAMPLING)) + { + // Recompute the number of components to account for color difference component subsampling + component_count = codec->pattern_width * codec->pattern_height + 2; + } +#endif + + // Compute the padding (in bytes) from the end of the component transform to the end of the chunk payload + padding = (int)((chunk_size * sizeof(SEGMENT)) - component_count * sizeof(uint8_t)); + + for (i = 0; i < component_count; i++) + { + int value; + + value = GetBits(stream, 8); + + //TODO: Need to save the permutation index in yhe codec state + (void)value; + } + + // Skip the padding at the end of the chunk payload + GetBits(stream, 8 * padding); + + // Should be at the end of the last segment in the chunk + assert(IsAlignedSegment(stream)); + + return CODEC_ERROR_OKAY; +} diff --git a/source/lib/vc5_decoder/component.h b/source/lib/vc5_decoder/component.h new file mode 100755 index 0000000..db208b0 --- /dev/null +++ b/source/lib/vc5_decoder/component.h @@ -0,0 +1,36 @@ +/*! @file component.h + * + * @brief Declaration of routines for the inverse component transform and permutation. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMPONENT_H +#define COMPONENT_H + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR ParseInverseComponentTransform(DECODER *decoder, BITSTREAM *stream, size_t chunk_size); + + CODEC_ERROR ParseInverseComponentPermutation(DECODER *decoder, BITSTREAM *stream, size_t chunk_size); + +#ifdef __cplusplus +} +#endif + +#endif // COMPONENT_H diff --git a/source/lib/vc5_decoder/decoder.c b/source/lib/vc5_decoder/decoder.c new file mode 100755 index 0000000..73aa3b8 --- /dev/null +++ b/source/lib/vc5_decoder/decoder.c @@ -0,0 +1,2310 @@ +/*! @file decoder.h + * + * @brief Implementation of core decoder functions and data structure + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +/*! + @brief Align the bitstream to a byte boundary + + Enough bits are removed from the bitstream buffer to + align the bitstream to the next byte. + */ +static CODEC_ERROR AlignBitsByte(BITSTREAM *bitstream) +{ + // Compute the number of bits to skip + BITCOUNT count = bitstream->count % 8; + GetBits(bitstream, count); + assert((bitstream->count % 8) == 0); + return CODEC_ERROR_OKAY; +} + +/*! + @brief Align the bitstream to the next word boundary + + All of the bits in the bitstream buffer are flushed unless + the bitstream buffer is completely empty or completely full. + */ +static CODEC_ERROR AlignBitsWord(BITSTREAM *bitstream) +{ + BITCOUNT count = bitstream->count; + + if (0 < count && count < bit_word_count) { + GetBits(bitstream, count); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Align the bitstream to the next tag value pair + */ +static CODEC_ERROR AlignBitsSegment(BITSTREAM *bitstream) +{ + STREAM *stream = bitstream->stream; + size_t byte_count; + + // Byte align the bitstream + AlignBitsByte(bitstream); + assert((bitstream->count % 8) == 0); + + // Compute the number of bytes in the bit buffer + byte_count = bitstream->count / 8; + + // Add the number of bytes read from the stream + byte_count += stream->byte_count; + + while ((byte_count % sizeof(TAGVALUE)) != 0) + { + GetBits(bitstream, 8); + byte_count++; + } + + // The bitstream should be aligned to the next segment + assert((bitstream->count == 0) || (bitstream->count == bit_word_count)); + assert((byte_count % sizeof(TAGVALUE)) == 0); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Initialize the decoder data structure + This routine performs the same function as a C++ constructor. + The decoder is initialized with default values that are replaced + by the parameters used to prepare the decoder (see @ref PrepareDecoder). + This routine does not perform all of the initializations required + to prepare the decoder data structure for decoding a sample. + */ +CODEC_ERROR InitDecoder(DECODER *decoder, const gpr_allocator *allocator) +{ + assert(decoder != NULL); + if (! (decoder != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + memset(decoder, 0, sizeof(DECODER)); + + // Assign a memory allocator to the decoder + decoder->allocator = (gpr_allocator *)allocator; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Release resources allocated by the decoder + Note that this routine does not close the logfile. + */ +CODEC_ERROR ReleaseDecoder(DECODER *decoder) +{ + // Free the wavelet transforms and decoding buffers + ReleaseDecoderTransforms(decoder); + ReleaseDecoderBuffers(decoder); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Decode the bitstream encoded into the byte stream into separate component arrays + This is a convenience routine for applications that use the byte stream data structure + for bitstreams stored in a file or memory buffer. + The main entry point for decoding a bitstream is @ref DecodingProcess. + The parameters data structure is intended to simulate information that may be available + to the decoder from the media container or an external application. + This routine assumes that the unpacked image has already been initialized. + */ +CODEC_ERROR DecodeStream(STREAM *stream, UNPACKED_IMAGE *unpacked_image, const DECODER_PARAMETERS *parameters) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + BITSTREAM bitstream; + DECODER decoder; + + // Initialize the bitstream data structure + InitBitstream(&bitstream); + + // Bind the bitstream to the byte stream + error = AttachBitstream(&bitstream, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Decode the bitstream sample into a image buffer + error = DecodingProcess(&decoder, &bitstream, unpacked_image, parameters); + + // Release any resources allocated by the decoder + ReleaseDecoder(&decoder); + + // Release any resources allocated by the bitstream + ReleaseBitstream(&bitstream); + + return error; +} + +/*! + @brief Decode the bitstream encoded into the byte stream + This is a convenience routine for applications that use the byte + stream data structure for samples stored in a file or memory buffer. + The main entry point for decoding a bitstream is @ref DecodingProcess. + The parameters data structure is intended to simulate information that + may be available to the decoder from the media container or an external + application. + */ +CODEC_ERROR DecodeImage(STREAM *stream, IMAGE *packed_image, RGB_IMAGE *rgb_image, DECODER_PARAMETERS *parameters) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + BITSTREAM bitstream; + DECODER decoder; + DIMENSION packed_width; + DIMENSION packed_height; + PIXEL_FORMAT packed_format; + + SetupDecoderLogCurve(); + + // The unpacked image will hold the component arrays decoded from the bitstream + UNPACKED_IMAGE unpacked_image; + + // Initialize the bitstream data structure + InitBitstream(&bitstream); + + // Bind the bitstream to the byte stream + error = AttachBitstream(&bitstream, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // The component arrays will be allocated after the bitstream is decoded + InitUnpackedImage(&unpacked_image); + + // Decode the bitstream sample into a image buffer + error = DecodingProcess(&decoder, &bitstream, &unpacked_image, parameters); + + if( error != CODEC_ERROR_OKAY ) + { + return error; + } + + switch (parameters->rgb_resolution) { + + case GPR_RGB_RESOLUTION_NONE: + // The dimensions and format for the output of the image packing process + SetOutputImageFormat(&decoder, parameters, &packed_width, &packed_height, &packed_format); + + // Allocate the image buffer for output of the image packing process + AllocImage(decoder.allocator, packed_image, packed_width, packed_height, packed_format); + + // Pack the component arrays into the output image + ImageRepackingProcess(&unpacked_image, packed_image, parameters); + break; + + case GPR_RGB_RESOLUTION_HALF: + WaveletToRGB(parameters->allocator, (PIXEL*)unpacked_image.component_array_list[0].data, (PIXEL*)unpacked_image.component_array_list[1].data, (PIXEL*)unpacked_image.component_array_list[2].data, + unpacked_image.component_array_list[2].width, unpacked_image.component_array_list[2].height, unpacked_image.component_array_list[2].pitch / 2, + rgb_image, 12, parameters->rgb_bits, ¶meters->rgb_gain ); + break; + + case GPR_RGB_RESOLUTION_QUARTER: + + WaveletToRGB(parameters->allocator, decoder.transform[0].wavelet[0]->data[0], decoder.transform[1].wavelet[0]->data[0], decoder.transform[2].wavelet[0]->data[0], + decoder.transform[2].wavelet[0]->width, decoder.transform[2].wavelet[0]->height, decoder.transform[2].wavelet[0]->width, + rgb_image, 14, parameters->rgb_bits, ¶meters->rgb_gain ); + break; + + case GPR_RGB_RESOLUTION_EIGHTH: + + WaveletToRGB(parameters->allocator, decoder.transform[0].wavelet[1]->data[0], decoder.transform[1].wavelet[1]->data[0], decoder.transform[2].wavelet[1]->data[0], + decoder.transform[2].wavelet[1]->width, decoder.transform[2].wavelet[1]->height, decoder.transform[2].wavelet[1]->width, + rgb_image, 14, parameters->rgb_bits, ¶meters->rgb_gain ); + + break; + + case GPR_RGB_RESOLUTION_SIXTEENTH: + + WaveletToRGB(parameters->allocator, decoder.transform[0].wavelet[2]->data[0], decoder.transform[1].wavelet[2]->data[0], decoder.transform[2].wavelet[2]->data[0], + decoder.transform[2].wavelet[2]->width, decoder.transform[2].wavelet[2]->height, decoder.transform[2].wavelet[2]->width, + rgb_image, 14, parameters->rgb_bits, ¶meters->rgb_gain ); + break; + + default: + return CODEC_ERROR_UNSUPPORTED_FORMAT; + break; + } + + ReleaseComponentArrays( ¶meters->allocator, &unpacked_image, unpacked_image.component_count ); + + // Release any resources allocated by the decoder + ReleaseDecoder(&decoder); + + // Release any resources allocated by the bitstream + ReleaseBitstream(&bitstream); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Initialize the decoder using the specified parameters + @todo Add more error checking to this top-level routine + */ +CODEC_ERROR PrepareDecoder(DECODER *decoder, const DECODER_PARAMETERS *parameters) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Initialize the decoder data structure + error = InitDecoder(decoder, ¶meters->allocator); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Set the mask that specifies which parts of the VC-5 standard are supported + decoder->enabled_parts = parameters->enabled_parts; + + // Verify that the enabled parts are correct + error = VerifyEnabledParts(decoder->enabled_parts); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + + // Initialize the codec state (allocation routines use the codec state) + error = PrepareDecoderState(decoder, parameters); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + if (parameters != NULL) + { +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + decoder->layer_count = (uint_fast8_t)parameters->layer_count; + decoder->progressive = parameters->progressive; + decoder->top_field_first = parameters->top_field_first; +#endif + } + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (IsPartEnabled(decoder->enabled_parts, VC5_PART_SECTIONS)) + { + decoder->section_flag = parameters->section_flag; + } +#endif + + decoder->subbands_to_decode = MAX_SUBBAND_COUNT; + + return error; +} + +/*! + @brief Decode a VC-5 bitstream to an ordered set of component arrays + This is the main entry point for decoding a sample. The decoder must + have been initialized by a call to @ref PrepareDecoder. + The bitstream must be initialized and bound to a byte stream before + calling this routine. The unpacked output image will be initialized by this + routine to hold the decoded component arrays represented in the bitstream. + @todo When the VC-5 part for layers is defined, should be able to pass a mask + indicating which layers must be decoded + */ +CODEC_ERROR DecodingProcess(DECODER *decoder, BITSTREAM *stream, UNPACKED_IMAGE *image, const DECODER_PARAMETERS *parameters) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + TAGVALUE segment; + + // Initialize the decoder with a default allocator + PrepareDecoder(decoder, parameters); + + // Get the bitstream start marker + segment = GetSegment(stream); + if (segment.longword != StartMarkerSegment) + { + return CODEC_ERROR_MISSING_START_MARKER; + } + + // Set up number of subbands to decode + if( parameters->rgb_resolution == GPR_RGB_RESOLUTION_SIXTEENTH ) + { + decoder->subbands_to_decode = 1; + } + else if( parameters->rgb_resolution == GPR_RGB_RESOLUTION_EIGHTH ) + { + decoder->subbands_to_decode = 4; + } + else if( parameters->rgb_resolution == GPR_RGB_RESOLUTION_QUARTER ) + { + decoder->subbands_to_decode = 7; + } + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + // Decode each layer in the sample + if (decoder->layer_count > 1) + { + IMAGE decoded_image[MAX_LAYER_COUNT]; + PIXEL_FORMAT decoded_format = decoder->output.format; + int layer_index; + + for (layer_index = 0; layer_index < decoder->layer_count; layer_index++) + { + DIMENSION layer_width = LayerWidth(decoder, decoder->output.width); + DIMENSION layer_height = LayerHeight(decoder, decoder->output.height); + + // Allocate a image for this layer + AllocImage(decoder->allocator, &decoded_image[layer_index], layer_width, layer_height, decoded_format); + + // Decode the layer into its own image + error = DecodeSampleLayer(decoder, stream, &decoded_image[layer_index]); + if (error != CODEC_ERROR_OKAY) { + break; + } + } + + if (error == CODEC_ERROR_OKAY) + { + // The decoded image in each layer is composited into the output image + error = ReconstructSampleFrame(decoder, decoded_image, decoder->layer_count, image); + } + + // Free the images used for decoding each layer + for (layer_index = 0; layer_index < decoder->layer_count; layer_index++) + { + ReleaseImage(decoder->allocator, &decoded_image[layer_index]); + } + } + else +#endif + { + // A VC-5 Part 1 bitstream can only contain a single layer (encoded image) + error = DecodeSingleImage(decoder, stream, image, parameters); + } + + // Done decoding all layers in the sample and computing the output image + return error; +} + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) +/*! + @brief Decode the portion of a sample that corresponds to a single layer + Samples can be contain multiple subsamples. Each subsample may correspond to + a different view. For example, an encoded video sample may contain both the + left and right subsamples in a stereo pair. + Subsamples have been called tracks or channels, but this terminology can be + confused with separate video tracks in a multimedia container or the color + planes that are called channels elsewhere in this codec. + The subsamples are decoded seperately and composited to form a single image + that is the output of the complete process of decoding a single video sample. + For this reason, the subsamples are called layers. + @todo Okay to call a subsample a layer? + */ +CODEC_ERROR DecodeSampleLayer(DECODER *decoder, BITSTREAM *input, UNPACKED_IMAGE *image) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Initialize the codec state (including the dimensions of the first wavelet band) + PrepareDecoderState(decoder, NULL); + + // Reset the flags in the wavelet transforms + PrepareDecoderTransforms(decoder); + + // Process tag value pairs until the layer has been decoded + for (;;) + { + TAGVALUE segment; + + // Read the next tag value pair from the bitstream + segment = GetSegment(input); + assert(input->error == BITSTREAM_ERROR_OKAY); + if (input->error != BITSTREAM_ERROR_OKAY) { + decoder->error = CodecErrorBitstream(input->error); + return decoder->error; + break; + } + + error = UpdateCodecState(decoder, input, segment); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Processed all wavelet bands in all channels? + if (IsLayerComplete(decoder)) break; + + } + + // Parsed the bitstream without errors? + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Reconstruct the output image using the last decoded wavelet in each channel + return ReconstructLayerImage(decoder, image); +} +#endif + +/*! + @brief Decode the bitstream into a list of component arrays + */ +CODEC_ERROR DecodeSingleImage(DECODER *decoder, BITSTREAM *input, UNPACKED_IMAGE *image, const DECODER_PARAMETERS *parameters) +{ + TIMESTAMP("[BEG]", 2) + + CODEC_ERROR error = CODEC_ERROR_OKAY; + + CODEC_STATE *codec = &decoder->codec; + + // Process tag value pairs until the layer has been decoded + for (;;) + { + TAGVALUE segment; + + // Read the next tag value pair from the bitstream + segment = GetSegment(input); + assert(input->error == BITSTREAM_ERROR_OKAY); + if (input->error != BITSTREAM_ERROR_OKAY) { + decoder->error = CodecErrorBitstream(input->error); + return decoder->error; + break; + } + + error = UpdateCodecState(decoder, input, segment); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Processed all wavelet bands in all channels? + if ( IsDecodingComplete(decoder) && codec->header == false ) { + break; + } + } + + // Parsed the bitstream without errors? + if (error != CODEC_ERROR_OKAY) { + return error; + } + + TIMESTAMP("[END]", 2) + + if( parameters->rgb_resolution == GPR_RGB_RESOLUTION_NONE || + parameters->rgb_resolution == GPR_RGB_RESOLUTION_HALF || + parameters->rgb_resolution == GPR_RGB_RESOLUTION_FULL ) + { + // Reconstruct the output image using the last decoded wavelet in each channel + error = ReconstructUnpackedImage(decoder, image); + } + + return error; +} + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) +/*! + @brief Set the channel dimensions from the image dimensions and format + This routine is used set the channel dimensions and other channel-specific + parameters after the bitstream header has been parsed. The dimensions of + each channel can only be set using the parameters present in the bitstream + header if the bitstream conforms to VC-5 Part 3. + */ +CODEC_ERROR SetImageChannelParameters(DECODER *decoder, int channel_number) +{ + CODEC_STATE *codec = &decoder->codec; + IMAGE_FORMAT image_format = codec->image_format; + DIMENSION image_width = codec->image_width; + DIMENSION image_height = codec->image_height; + DIMENSION pattern_width = codec->pattern_width; + DIMENSION pattern_height = codec->pattern_height; + + // Are the image dimensions valid? + if (image_width == 0 || image_height == 0) + { + // Cannot set the channel dimensions without valid image dimensions + return CODEC_ERROR_IMAGE_DIMENSIONS; + } + + // Are the pattern dimensions valid? + if (pattern_width == 0 || pattern_height == 0) + { + // The channel dimensions may depend on the pattern dimensions + return CODEC_ERROR_PATTERN_DIMENSIONS; + } + + switch (image_format) + { + case IMAGE_FORMAT_RAW: + // The pattern width and height must be two + assert(pattern_width == 2 && pattern_height == 2); + + // The image dimensions must be divisible by the pattern dimensions + //assert((image_width % 2) == 0 && (image_height % 2) == 0); + + decoder->channel[channel_number].width = image_width / 2; + decoder->channel[channel_number].height = image_height / 2; + break; + + default: + // Cannot set the channel dimensions without a valid image format + return CODEC_ERROR_BAD_IMAGE_FORMAT; + break; + } + + //TODO: Is the default bits per component the correct value to use? + decoder->channel[channel_number].bits_per_component = codec->bits_per_component; + decoder->channel[channel_number].initialized = true; + + return CODEC_ERROR_OKAY; +} +#endif + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) +/*! + @brief Allocate all of the wavelets used during decoding + This routine allocates all of the wavelets in the wavelet tree that + may be used during decoding. + This routine is used to preallocate the wavelets before decoding begins. + If the wavelet bands are allocated on demand if not preallocated. + By default, the wavelet bands are encoded into the bitstream with the bands + from the wavelet at the highest level (smallest wavelet) first so that the + bands can be processed by the decoder in the order as the sample is decoded. + */ +CODEC_ERROR AllocDecoderTransforms(DECODER *decoder) +{ + CODEC_ERROR result; + // Use the default allocator for the decoder + + gpr_allocator *allocator = decoder->allocator; + int channel_number; + int wavelet_index; + + int channel_count; + int wavelet_count; + + assert(decoder != NULL); + if (! (decoder != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + channel_count = decoder->codec.channel_count; + wavelet_count = decoder->wavelet_count; + + for (channel_number = 0; channel_number < channel_count; channel_number++) + { + DIMENSION wavelet_width; + DIMENSION wavelet_height; + + // Set the channel dimensions using the information obtained from the bitstream header + result = SetImageChannelParameters(decoder, channel_number); + if( result != CODEC_ERROR_OKAY ) + { + assert(0); + return result; + } + + // Check that the channel dimensions and other parameters have been set + assert(decoder->channel[channel_number].initialized); + + // The dimensions of the wavelet at level zero are equal to the channel dimensions + wavelet_width = decoder->channel[channel_number].width; + wavelet_height = decoder->channel[channel_number].height; + + for (wavelet_index = 0; wavelet_index < wavelet_count; wavelet_index++) + { + WAVELET *wavelet; + + // Pad the wavelet width if necessary + if ((wavelet_width % 2) != 0) { + wavelet_width++; + } + + // Pad the wavelet height if necessary + if ((wavelet_height % 2) != 0) { + wavelet_height++; + } + + // Dimensions of the current wavelet must be divisible by two + assert((wavelet_width % 2) == 0 && (wavelet_height % 2) == 0); + + // Reduce the dimensions of the next wavelet by half + wavelet_width /= 2; + wavelet_height /= 2; + + wavelet = CreateWavelet(allocator, wavelet_width, wavelet_height); + decoder->transform[channel_number].wavelet[wavelet_index] = wavelet; + } + } + + return CODEC_ERROR_OKAY; +} +#endif + +/*! + @brief Free the wavelet transforms allocated by the decoder + */ +CODEC_ERROR ReleaseDecoderTransforms(DECODER *decoder) +{ + int channel_count = decoder->codec.channel_count; + int channel_index; + + for (channel_index = 0; channel_index < channel_count; channel_index++) + { + int wavelet_index; + + for (wavelet_index = 0; wavelet_index < decoder->wavelet_count; wavelet_index++) + { + WAVELET *wavelet = decoder->transform[channel_index].wavelet[wavelet_index]; + DeleteWavelet(decoder->allocator, wavelet); + } + } + + return CODEC_ERROR_OKAY; +} + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) +/*! + @brief Allocate all of the buffers required for decoding + This routine allocates buffers required for decoding, not including + the wavelet images in the wavelet tree which are allocated by + @ref AllocDecoderTransforms + This routine is used to preallocate buffers before decoding begins. + Decoding buffers are allocated on demand if not preallocated. + Currently, the reference decoder allocates scratch buffers as required + by each routine that needs scratch space and the scratch buffers are + deallocated at the end each routine that allocates scratch space. + @todo Should it be an error if the buffers are not preallocated? + */ +CODEC_ERROR AllocDecoderBuffers(DECODER *decoder) +{ + (void)decoder; + return CODEC_ERROR_UNIMPLEMENTED; +} +#endif + +/*! + @brief Free any buffers allocated by the decoder + */ +CODEC_ERROR ReleaseDecoderBuffers(DECODER *decoder) +{ + (void)decoder; + return CODEC_ERROR_UNIMPLEMENTED; +} + +/*! + @brief Allocate the wavelets for the specified channel + */ +CODEC_ERROR AllocateChannelWavelets(DECODER *decoder, int channel_number) +{ + // Use the default allocator for the decoder + gpr_allocator *allocator = decoder->allocator; + int wavelet_index; + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + // Use the channel dimensions computed from the image dimension and image format + DIMENSION channel_width = decoder->channel[channel_number].width; + DIMENSION channel_height = decoder->channel[channel_number].height; +#else + // Use the channel dimensions from the current codec state + DIMENSION channel_width = decoder->codec.channel_width; + DIMENSION channel_height = decoder->codec.channel_height; +#endif + + // Round up the wavelet dimensions to an even number + DIMENSION wavelet_width = ((channel_width % 2) == 0) ? channel_width / 2 : (channel_width + 1) / 2; + DIMENSION wavelet_height = ((channel_height % 2) == 0) ? channel_height / 2 : (channel_height + 1) / 2; + + //TODO: Check for errors before the code that initializes the local variables + assert(decoder != NULL); + if (! (decoder != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + for (wavelet_index = 0; wavelet_index < decoder->wavelet_count; wavelet_index++) + { + WAVELET *wavelet = decoder->transform[channel_number].wavelet[wavelet_index]; + + // Has a wavelet already been created? + if (wavelet != NULL) + { + // Is the wavelet the correct size? + if (wavelet_width != wavelet->width || + wavelet_height != wavelet->height) + { + // Deallocate the wavelet + DeleteWavelet(allocator, wavelet); + + wavelet = NULL; + } + } + + if (wavelet == NULL) + { + wavelet = CreateWavelet(allocator, wavelet_width, wavelet_height); + assert(wavelet != NULL); + + decoder->transform[channel_number].wavelet[wavelet_index] = wavelet; + } + + // Pad the wavelet width if necessary + if ((wavelet_width % 2) != 0) { + wavelet_width++; + } + + // Pad the wavelet height if necessary + if ((wavelet_height % 2) != 0) { + wavelet_height++; + } + + // Dimensions of the current wavelet must be divisible by two + assert((wavelet_width % 2) == 0 && (wavelet_height % 2) == 0); + + // Reduce the dimensions of the next wavelet by half + wavelet_width /= 2; + wavelet_height /= 2; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Initialize the codec state before starting to decode a bitstream + */ +CODEC_ERROR PrepareDecoderState(DECODER *decoder, const DECODER_PARAMETERS *parameters) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + CODEC_STATE *codec = &decoder->codec; + + // Set the parameters that control the decoding process + decoder->wavelet_count = 3; + + // The wavelets and decoding buffers have not been allocated + decoder->memory_allocated = false; + + // Clear the table of information about each decoded channel + memset(decoder->channel, 0, sizeof(decoder->channel)); + + // Set the codebook + decoder->codebook = (CODEBOOK *)decoder_codeset_17.codebook; + + // Initialize the codec state with the default parameter values + error = PrepareCodecState(codec); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Initialize the codec state with the external parameter values + codec->image_width = parameters->input.width; + codec->image_height = parameters->input.height; + + //TODO: Initialize other parameters with external values? + + // The default channel dimensions are the image dimensions + codec->channel_width = codec->image_width; + codec->channel_height = codec->image_height; + + return error; +} + +/*! + @brief Prepare the decoder transforms for the next layer + Each wavelet in the decoder transforms contain flags that indicate + whether the wavelet bands must be decoded. These flags must be reset + before decoding the next layer. + */ +CODEC_ERROR PrepareDecoderTransforms(DECODER *decoder) +{ + int channel_count = decoder->codec.channel_count; + int channel_index; + + for (channel_index = 0; channel_index < channel_count; channel_index++) + { + int wavelet_count = decoder->wavelet_count; + int wavelet_index; + + for (wavelet_index = 0; wavelet_index < wavelet_count; wavelet_index++) + { + WAVELET *wavelet = decoder->transform[channel_index].wavelet[wavelet_index]; + wavelet->valid_band_mask = 0; + } + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Pack the component arrays into the output image + + The decoding process outputs a set of component arrays that does not correspond + to any common image format. The image repacking process converts the ordered + set of component arrays output by the decoding processing into a packed image. + The image repacking process is not normative in VC-5 Part 1. + */ +CODEC_ERROR ImageRepackingProcess(const UNPACKED_IMAGE *unpacked_image, + PACKED_IMAGE *packed_image, + const DECODER_PARAMETERS *parameters) +{ + DIMENSION output_width = packed_image->width; + DIMENSION output_height = packed_image->height; + size_t output_pitch = packed_image->pitch; + PIXEL_FORMAT output_format = packed_image->format; + PIXEL *output_buffer = packed_image->buffer; + ENABLED_PARTS enabled_parts = parameters->enabled_parts; + + (void)parameters; + + // The dimensions must be in units of Bayer pattern elements + output_width /= 2; + output_height /= 2; + output_pitch *= 2; + + switch (output_format) + { + case PIXEL_FORMAT_RAW_RGGB_12: + case PIXEL_FORMAT_RAW_GBRG_12: + return PackComponentsToRAW(unpacked_image, output_buffer, output_pitch, + output_width, output_height, enabled_parts, 12, output_format ); + + case PIXEL_FORMAT_RAW_RGGB_14: + case PIXEL_FORMAT_RAW_GBRG_14: + return PackComponentsToRAW(unpacked_image, output_buffer, output_pitch, + output_width, output_height, enabled_parts, 14, output_format ); + break; + + case PIXEL_FORMAT_RAW_RGGB_16: + return PackComponentsToRAW(unpacked_image, output_buffer, output_pitch, + output_width, output_height, enabled_parts, 16, output_format ); + break; + + default: + assert(0); + break; + } + + // Unsupported output image format + return CODEC_ERROR_UNSUPPORTED_FORMAT; +} + +/*! + @brief Compute default parameters for the repacked image + */ +CODEC_ERROR SetOutputImageFormat(DECODER *decoder, + const DECODER_PARAMETERS *parameters, + DIMENSION *width_out, + DIMENSION *height_out, + PIXEL_FORMAT *format_out) +{ + // The image dimensions are in units of samples + DIMENSION output_width = decoder->codec.image_width; + DIMENSION output_height = decoder->codec.image_height; + + PIXEL_FORMAT output_format = PIXEL_FORMAT_UNKNOWN; + + // Override the pixel format with the format passed as a parameter + if (parameters->output.format != PIXEL_FORMAT_UNKNOWN) { + output_format = parameters->output.format; + } + assert(output_format != PIXEL_FORMAT_UNKNOWN); + + if (width_out != NULL) { + *width_out = output_width; + } + + if (height_out != NULL) { + *height_out = output_height; + } + + if (format_out != NULL) { + *format_out = output_format; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Return true if the lowpass bands in all channels are valid + */ +bool ChannelLowpassBandsAllValid(const DECODER *decoder, int index) +{ + int channel_count = decoder->codec.channel_count; + int channel; + for (channel = 0; channel < channel_count; channel++) + { + WAVELET *wavelet = decoder->transform[channel].wavelet[index]; + if ((wavelet->valid_band_mask & BandValidMask(0)) == 0) { + return false; + } + } + + // All channels have valid lowpass bands at the specified level + return true; +} + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + +/* + @brief Return true if the tag identifies a section header + */ +bool IsSectionHeader(TAGWORD tag) +{ + switch (tag) + { + case CODEC_TAG_ImageSectionTag: + case CODEC_TAG_HeaderSectionTag: + case CODEC_TAG_LayerSectionTag: + case CODEC_TAG_ChannelSectionTag: + case CODEC_TAG_WaveletSectionTag: + case CODEC_TAG_SubbandSectionTag: + return true; + + default: + return false; + } + + return false; +} + +/* + @brief Map the tag for a section header to the section number + */ +CODEC_ERROR GetSectionNumber(TAGWORD tag, int *section_number_out) +{ + int section_number = 0; + + switch (tag) + { + case CODEC_TAG_ImageSectionTag: + section_number = 1; + break; + + case CODEC_TAG_HeaderSectionTag: + section_number = 2; + break; + + case CODEC_TAG_LayerSectionTag: + section_number = 3; + break; + + case CODEC_TAG_ChannelSectionTag: + section_number = 4; + break; + + case CODEC_TAG_WaveletSectionTag: + section_number = 5; + break; + + case CODEC_TAG_SubbandSectionTag: + section_number = 6; + break; + + default: + assert(0); + break; + } + + if (section_number_out != NULL) { + *section_number_out = section_number; + } + + if (section_number > 0) { + return CODEC_ERROR_OKAY; + } + + return CODEC_ERROR_BAD_SECTION_TAG; +} + +/*! + @brief Write section information to the section log file + */ +CODEC_ERROR WriteSectionInformation(FILE *logfile, int section_number, int section_length) +{ + fprintf(logfile, "Section: %d, length: %d\n", section_number, section_length); + return CODEC_ERROR_OKAY; +} + +#endif + +/*! + @brief Skip the payload in a chunk + + A chunk is a tag value pair where the value specifies the length + of a payload. If the tag is a negative number, then the payload + can be skipped without affecting the decoding process. + */ +static CODEC_ERROR SkipPayload(BITSTREAM *bitstream, int chunk_size) +{ + // The chunk size is in units of 32-bit words + size_t size = 4 * chunk_size; + + // This routine assumes that the bit buffer is empty + assert(bitstream->count == 0); + + // Skip the specified number of bytes in the stream + return SkipBytes(bitstream->stream, size); +} + +/*! + @brief Parse the unique image identifier in a small chunk payload + + @todo Should the UMID instance number be a parameter to this routine? + */ +static CODEC_ERROR ParseUniqueImageIdentifier(DECODER *decoder, BITSTREAM *stream, size_t identifier_length) +{ + const int UMID_length_byte = 0x13; + const int UMID_instance_number = 0; + + // Total length of the unique image identifier chunk payload (in segments) + const int identifier_chunk_payload_length = UMID_length + sequence_number_length; + + uint8_t byte_array[12]; + BITWORD length_byte; + BITWORD instance_number; + + // Check that the chunk payload has the correct length (in segments) + if (identifier_length != identifier_chunk_payload_length) { + return CODEC_ERROR_SYNTAX_ERROR; + } + + // The unique image identifier chunk should begin with a UMID label + GetByteArray(stream, byte_array, sizeof(byte_array)); + if (memcmp(byte_array, UMID_label, sizeof(UMID_label)) != 0) { + return CODEC_ERROR_UMID_LABEL; + } + + // Check the UMID length byte + length_byte = GetBits(stream, 8); + if (length_byte != UMID_length_byte) { + return CODEC_ERROR_SYNTAX_ERROR; + } + + // Check the UMID instance number + instance_number = GetBits(stream, 24); + if (instance_number != UMID_instance_number) { + return CODEC_ERROR_SYNTAX_ERROR; + } + + // Read the image sequence identifier + GetByteArray(stream, decoder->image_sequence_identifier, sizeof(decoder->image_sequence_identifier)); + + // Read the image sequence number + decoder->image_sequence_number = GetBits(stream, 32); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Update the codec state with the specified tag value pair + When a segment (tag value pair) is encountered in the bitstream of an + encoded sample, it may imply some change in the codec state. For example, + when a tag for the encoded format is read from the bitstream, the encoded + format entry in the codec state may change. + Some tags require that additional information must be read from the + bitstream and more segments may be encountered, leading to additional + changes in the codec state. + A tag may identify a single parameter and the parameter value must be updated + in the codec state with the new value specified in the segment, but a tag may + also imply that other pparameter values must be updated. For example, the tag + that marks the first encounter with a wavelet at a lower level in the wavelet + tree implies that the width and height of wavelet bands that may be encoded in + the remainder of the sample must be doubled. + + It is not necessary for the encoder to insert segments into the bitstream if the + codec state change represented by an encoded tag and value can be deduced from + earlier segments in the bitstream and the codec state can be changed at a time + during decoding that is functionally the same as when the state change would have + been performed by an explicitly encoded tag and value. + @todo Need to check that parameters found in the sample are consistent with + the decoding parameters used to initialize the codec state. + */ +CODEC_ERROR UpdateCodecState(DECODER *decoder, BITSTREAM *stream, TAGVALUE segment) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + CODEC_STATE *codec = &decoder->codec; + ENABLED_PARTS enabled_parts = decoder->enabled_parts; + bool optional = false; + int chunk_size = 0; + TAGWORD tag = segment.tuple.tag; + TAGWORD value = segment.tuple.value; + + // The enabled parts variable may not be used depending on the compile-time options + (void)enabled_parts; + + // Assume that the next syntax element is not a tag-value pair for a header parameter + codec->header = false; + + // Assume that the next syntax element is not a codeblock (large chunk element) + codec->codeblock = false; + + // Is this an optional tag? + if (tag < 0) { + tag = RequiredTag(tag); + optional = true; + } + + switch (tag) + { + case CODEC_TAG_ChannelCount: // Number of channels in the transform + assert(0 < value && value <= MAX_CHANNEL_COUNT); + codec->channel_count = (uint_least8_t)value; + codec->header = true; + break; + + case CODEC_TAG_ImageWidth: // Width of the image + codec->image_width = value; + codec->header = true; + + // The image width is the default width of the next channel in the bitstream + codec->channel_width = value; + break; + + case CODEC_TAG_ImageHeight: // Height of the image + codec->image_height = value; + codec->header = true; + + // The image height is the default height of the next channel in the bitstream + codec->channel_height = value; + break; + + case CODEC_TAG_SubbandNumber: // Subband number of this wavelet band + codec->subband_number = value; + break; + + case CODEC_TAG_Quantization: // Quantization applied to band + codec->band.quantization = value; + break; + + case CODEC_TAG_LowpassPrecision: // Number of bits per lowpass coefficient + if (! (PRECISION_MIN <= value && value <= PRECISION_MAX)) { + return CODEC_ERROR_LOWPASS_PRECISION; + } + codec->lowpass_precision = (PRECISION)value; + break; + + case CODEC_TAG_ChannelNumber: // Channel number + codec->channel_number = value; + break; + + case CODEC_TAG_BitsPerComponent: // Number of bits in the video source + codec->bits_per_component = (PRECISION)value; + //error = SetDecoderBitsPerComponent(decoder, codec->channel_number, codec->bits_per_component); + break; + + case CODEC_TAG_PrescaleShift: + UpdatePrescaleTable(codec, value); + break; + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + case CODEC_TAG_ImageFormat: + if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS)) + { + codec->image_format = (IMAGE_FORMAT)value; + codec->header = true; + } + else + { + // The image format shall not be present in the bitstream + assert(0); + error = CODEC_ERROR_BITSTREAM_SYNTAX; + } + break; + + case CODEC_TAG_PatternWidth: + if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS)) + { + codec->pattern_width = (DIMENSION)value; + codec->header = true; + } + else + { + // The pattern width shall not be present in the bitstream + assert(0); + error = CODEC_ERROR_BITSTREAM_SYNTAX; + } + break; + + case CODEC_TAG_PatternHeight: + if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS)) + { + codec->pattern_height = (DIMENSION)value; + codec->header = true; + } + else + { + // The pattern height shall not be present in the bitstream + assert(0); + error = CODEC_ERROR_BITSTREAM_SYNTAX; + } + break; + + case CODEC_TAG_ComponentsPerSample: + if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS)) + { + codec->components_per_sample = (DIMENSION)value; + codec->header = true; + } + else + { + // The components per sample shall not be present in the bitstream + assert(0); + error = CODEC_ERROR_BITSTREAM_SYNTAX; + } + break; + + case CODEC_TAG_MaxBitsPerComponent: + if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS)) + { + codec->max_bits_per_component = (PRECISION)value; + codec->header = true; + } + else + { + // The components per sample shall not be present in the bitstream + assert(0); + error = CODEC_ERROR_BITSTREAM_SYNTAX; + } + break; +#endif + + case CODEC_TAG_ChannelWidth: +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS)) + { + // The channel width shall not be present in the bitstream + assert(0); + error = CODEC_ERROR_BITSTREAM_SYNTAX; + } + else +#endif + { + // The channel width may be present in the bitstream + codec->channel_width = (DIMENSION)value; + } + break; + + case CODEC_TAG_ChannelHeight: +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS)) + { + // The channel height shall not be present in the bitstream + assert(0); + error = CODEC_ERROR_BITSTREAM_SYNTAX; + } + else +#endif + { + // The channel height may be present in the bitstream + codec->channel_height = (DIMENSION)value; + } + break; + + default: // Unknown tag or the tag identifies a chunk + + //TODO: Check for chunk tags that are not defined in VC-5 Part 1 + + // Does this tag indicate a chunk of data? + if (tag & CODEC_TAG_CHUNK_MASK) + { + // Does this chunk have a 24-bit size? + if(tag & CODEC_TAG_LARGE_CHUNK) + { + // The chunk size includes the low byte in the tag + chunk_size = (value & 0xFFFF); + chunk_size += ((tag & 0xFF) << 16); + } + else + { + // The chunk size is specified by the value + chunk_size = (value & 0xFFFF); + } + } + + // Is this a codeblock? + if ((tag & CODEC_TAG_LargeCodeblock) == CODEC_TAG_LargeCodeblock) + { + codec->codeblock = true; + } + + // Is this chunk a unique image identifier? + else if (tag == CODEC_TAG_UniqueImageIdentifier) + { + // The unique image identifier should be optional + assert(optional); + if (! optional) { + return CODEC_ERROR_SYNTAX_ERROR; + } + + // Parse the unique image identifier + error = ParseUniqueImageIdentifier(decoder, stream, chunk_size); + } + + // Is this chunk an inverse component transform? + else if (tag == CODEC_TAG_InverseTransform) + { + // The inverse component transform should not be optional + assert(!optional); + if (optional) { + return CODEC_ERROR_SYNTAX_ERROR; + } + + // Parse the inverse component transform + error = ParseInverseComponentTransform(decoder, stream, chunk_size); + } + + // Is this chunk an inverse component permutation? + else if (tag == CODEC_TAG_InversePermutation) + { + // The inverse component permutation should not be optional + assert(!optional); + if (optional) { + return CODEC_ERROR_SYNTAX_ERROR; + } + + // Parse the inverse component permutation + error = ParseInverseComponentPermutation(decoder, stream, chunk_size); + } + + // Is this chunk a 16-bit inverse component transform? + else if (tag == CODEC_TAG_InverseTransform16) + { + // The 16-bit inverse component transform is not supported + assert(0); + return CODEC_ERROR_UNIMPLEMENTED; + } +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + // Is this a section header? + else if (IsPartEnabled(enabled_parts, VC5_PART_SECTIONS) && decoder->section_flag && IsSectionHeader(tag)) + { + int section_number; + + // Section headers are optional tag-value pairs + optional = true; + + // Is this a bitstream header section? + if (tag == CODEC_TAG_HeaderSectionTag) + { + // Handle this tag-value pair as if it was a bitstream header parameter + codec->header = true; + } + + // Convert the tag to a section number + GetSectionNumber(tag, §ion_number); + + // Record the section number and length (in segments) + codec->section_number = section_number; + codec->section_length = chunk_size; + + if( decoder->section_logfile ) + { + // Write the section information to the log file + WriteSectionInformation(decoder->section_logfile, section_number, chunk_size); + } + } +#endif + else + { + // Does this chunk have a 24-bit chunk payload size? + if (tag & CODEC_TAG_LARGE_CHUNK) + { + optional = true; + chunk_size = 0; + } + + assert(optional); + if (!optional) + { + error = CODEC_ERROR_BITSTREAM_SYNTAX; + } + else if (chunk_size > 0) + { + // Skip processing the payload of this optional chunk element + SkipPayload(stream, chunk_size); + } + } + break; + } + + // Encountered an error while processing the tag? + if (error != CODEC_ERROR_OKAY) + { + return error; + } + + //TODO: Check that bitstreams with missplaced header parameters fail to decode + + //if (IsHeaderParameter(tag)) + if (codec->header) + { + if (optional) + { +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (tag == CODEC_TAG_HeaderSectionTag) + { + // Okay for the bitstream header to contain an optional section header tag-value pair + } +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + else if (!IsPartEnabled(enabled_parts, VC5_PART_LAYERS)) + { + // A header parameter cannot be optional + error = CODEC_ERROR_REQUIRED_PARAMETER; + } +#endif +#endif +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + if (!IsPartEnabled(enabled_parts, VC5_PART_LAYERS)) + { + // A header parameter cannot be optional + error = CODEC_ERROR_REQUIRED_PARAMETER; + } +#endif + } + else if (decoder->header_finished) + { + // Should not encounter a header parameter after the header has been parsed + error = CODEC_ERROR_BITSTREAM_SYNTAX; + } + else + { + // Record that this header parameter has been decoded + error = UpdateHeaderParameter(decoder, tag); + } + } + else if (!decoder->header_finished) + { + // There should be no more header parameters in the bitstream + decoder->header_finished = true; + } + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + // The wavelets and buffers can be allocated after the bitstream header has been parsed + if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS) && + decoder->header_finished && + !decoder->memory_allocated) + { + // Allocate space for the wavelet transforms + AllocDecoderTransforms(decoder); + + // Allocate all buffers required for decoding + AllocDecoderBuffers(decoder); + + // Reset the flags in the wavelet transforms + PrepareDecoderTransforms(decoder); + + // The wavelet transforms and decoding buffers have been allocated + decoder->memory_allocated = true; + } +#endif + + // Found a codeblock element? + if (codec->codeblock) + { + const int channel_number = codec->channel_number; + + // Have the channel dimensions been initialized? + if (!decoder->channel[channel_number].initialized) + { + // Record the channel dimensions and component precision + decoder->channel[channel_number].width = codec->channel_width; + decoder->channel[channel_number].height = codec->channel_height; + + // Initialize the dimensions of this channel + decoder->channel[channel_number].initialized = true; + + //TODO: Allocate space for the wavelet transforms and decoding buffers + } + + // Is this the first codeblock encountered in the bitstream for this channel? + if (!decoder->channel[channel_number].found_first_codeblock) + { + // Remember the number of bits per component in this and higher numbered channel + decoder->channel[codec->channel_number].bits_per_component = codec->bits_per_component; + + // Found the first codeblock in the channel + decoder->channel[channel_number].found_first_codeblock = true; + } + + { + CODEC_STATE *codec = &decoder->codec; + + const int subband_number = codec->subband_number; + + if( subband_number < decoder->subbands_to_decode ) + { + // Decode the subband into its wavelet band + error = DecodeChannelSubband(decoder, stream, chunk_size); + } + else + { + // Skip decoding of subband + error = SkipPayload(stream, chunk_size); + + WAVELET* wavelet = decoder->transform[channel_number].wavelet[SubbandWaveletIndex(subband_number)]; + wavelet->valid_band_mask = 0xF; + } + + // Set the subband number for the next band expected in the bitstream + codec->subband_number++; + + // Was the subband successfully decoded? + if (error == CODEC_ERROR_OKAY) + { + // Record that this subband has been decoded successfully + SetDecodedBandMask(codec, subband_number); + } + + // Done decoding all subbands in this channel? + if (codec->subband_number == codec->subband_count) + { + // Advance to the next channel + codec->channel_number++; + + // Reset the subband number + codec->subband_number = 0; + } + } + + } + + return error; +} + +/*! + @brief Return true if the tag corresponds to a bitstream header parameter + */ +bool IsHeaderParameter(TAGWORD tag) +{ + switch (tag) + { + case CODEC_TAG_ImageWidth: + case CODEC_TAG_ImageHeight: + case CODEC_TAG_ChannelCount: + case CODEC_TAG_SubbandCount: + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + case CODEC_TAG_ImageFormat: + case CODEC_TAG_PatternWidth: + case CODEC_TAG_PatternHeight: + case CODEC_TAG_ComponentsPerSample: + case CODEC_TAG_MaxBitsPerComponent: +#endif + return true; + + default: + return false; + } +} + +/*! + @brief Return the header mask that corresponds to the header tag + */ +uint16_t GetHeaderMask(TAGWORD tag) +{ + uint16_t header_mask = 0; + + switch (tag) + { + case CODEC_TAG_ImageWidth: + header_mask = BITSTREAM_HEADER_FLAGS_IMAGE_WIDTH; + break; + + case CODEC_TAG_ImageHeight: + header_mask = BITSTREAM_HEADER_FLAGS_IMAGE_HEIGHT; + break; + + case CODEC_TAG_ChannelCount: + header_mask = BITSTREAM_HEADER_FLAGS_CHANNEL_COUNT; + break; + + case CODEC_TAG_SubbandCount: + header_mask = BITSTREAM_HEADER_FLAGS_SUBBAND_COUNT; + break; + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + case CODEC_TAG_ImageFormat: + header_mask = BITSTREAM_HEADER_FLAGS_IMAGE_FORMAT; + break; +#endif +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + case CODEC_TAG_PatternWidth: + header_mask = BITSTREAM_HEADER_FLAGS_PATTERN_WIDTH; + break; +#endif +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + case CODEC_TAG_PatternHeight: + header_mask = BITSTREAM_HEADER_FLAGS_PATTERN_HEIGHT; + break; +#endif +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + case CODEC_TAG_ComponentsPerSample: + header_mask = BITSTREAM_HEADER_FLAGS_COMPONENTS_PER_SAMPLE; + break; +#endif +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + case CODEC_TAG_MaxBitsPerComponent: + header_mask = BITSTREAM_HEADER_FLAGS_MAX_BITS_PER_COMPONENT; + break; +#endif + + default: + assert(0); + break; + } + + return header_mask; +} + +/*! + @brief Record that a header parameter was found in the bitstream. + The tag-value pair that corresponds to a header parameters must occur + in the bitstream header and must occur at most once in the bitstream. + */ +CODEC_ERROR UpdateHeaderParameter(DECODER *decoder, TAGWORD tag) +{ + uint16_t header_mask = 0; + + if (!IsHeaderParameter(tag)) { + return CODEC_ERROR_UNEXPECTED; + } + + header_mask = GetHeaderMask(tag); + + if (header_mask == 0) { + return CODEC_ERROR_UNEXPECTED; + } + + if (decoder->header_mask & header_mask) { + // The header parameter should occur at most once + return CODEC_ERROR_DUPLICATE_HEADER_PARAMETER; + } + + // Record this encounter with the header parameter + decoder->header_mask |= header_mask; + + return CODEC_ERROR_OKAY; +} + +#if VC5_ENABLED_PART(VC5_PART_COLOR_SAMPLING) +/*! + @brief Adjust the width of the layer (if necessary) + Note that all layers have the same dimensions so the layer index is not + passed as an argument to this routine. + All layers have the same width as the encoded width. + */ +DIMENSION LayerWidth(DECODER *decoder, DIMENSION width) +{ + //CODEC_STATE *codec = &decoder->codec; + (void)decoder; + return width; +} +#endif + +#if VC5_ENABLED_PART(VC5_PART_COLOR_SAMPLING) +/*! + @brief Adjust the height of the layer to account for interlaced frames + Note that all layers have the same dimensions so the layer index is not + passed as an argument to this routine. + */ +DIMENSION LayerHeight(DECODER *decoder, DIMENSION height) +{ + CODEC_STATE *codec = &decoder->codec; + + if (codec->progressive == 0) + { + height /= 2; + } + + return height; +} +#endif + +/*! + @brief Decode the specified wavelet subband + After decoded the specified subband, the routine checks whether all bands + in the current wavelet have been decoded and if so the inverse transform is + applied to the wavelet to reconstruct the lowpass band in the wavelet at the + next lower level. + */ +CODEC_ERROR DecodeChannelSubband(DECODER *decoder, BITSTREAM *input, size_t chunk_size) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + CODEC_STATE *codec = &decoder->codec; + + const int channel_number = codec->channel_number; + const int subband_number = codec->subband_number; + + // Get the index to the wavelet corresponding to this subband + const int index = SubbandWaveletIndex(subband_number); + + // Get the index of the wavelet band corresponding to this subband + const int band = SubbandBandIndex(subband_number); + + // Wavelet containing the band to decode + WAVELET *wavelet = NULL; + + //TODO: Need to check that the codeblock matches the chunk size + (void)chunk_size; + + // Allocate the wavelets for this channel if not already allocated + AllocateChannelWavelets(decoder, channel_number); + + // Is this a highpass band? + if (subband_number > 0) + { + // Decode a highpass band + + // Get the wavelet that contains the highpass band + wavelet = decoder->transform[channel_number].wavelet[index]; + + // The wavelets are preallocated + assert(wavelet != NULL); + + error = DecodeHighpassBand(decoder, input, wavelet, band); + if (error == CODEC_ERROR_OKAY) + { + // Update the wavelet band valid flags + UpdateWaveletValidBandMask(wavelet, band); + } + + // Save the quantization factor + wavelet->quant[band] = codec->band.quantization; + } + else + { + // Decode a lowpass band + + // Get the wavelet that contains the lowpass band + wavelet = decoder->transform[channel_number].wavelet[index]; + + // The lowpass band must be subband zero + assert(subband_number == 0); + + // The lowpass data is always stored in wavelet band zero + assert(band == 0); + + // The wavelets are preallocated + assert(wavelet != NULL); + + error = DecodeLowpassBand(decoder, input, wavelet); + if (error == CODEC_ERROR_OKAY) + { + // Update the wavelet band valid flags + UpdateWaveletValidBandMask(wavelet, band); + } + } + + // Ready to invert this wavelet to get the lowpass band in the lower wavelet? + if (BandsAllValid(wavelet)) + { + // Apply the inverse wavelet transform to reconstruct the lower level wavelet + error = ReconstructWaveletBand(decoder, channel_number, wavelet, index); + } + + return error; +} + +/*! + @brief Invert the wavelet to reconstruct a lowpass band + The bands in the wavelet at one level are used to compute the lowpass + band in the wavelet at the next lower level in the transform. Wavelet + levels are numbered starting at zero for the original image. The + reference codec for the baseline profile uses the classic wavelet + tree where each wavelet at a high level depends only on the wavelet + at the next lower level and each wavelet is a spatial wavelet with + four bands. + This routine is called during decoding after all bands in a wavelet + have been decoded and the lowpass band in the wavelet at the next + lower level can be computed by applying the inverse wavelet transform. + This routine is not called for the wavelet at level one to reconstruct the + decoded component arrays. Special routines are used to compute each component + array using the wavelet at level one in each channel. + + See @ref ReconstructUnpackedImage. + */ +CODEC_ERROR ReconstructWaveletBand(DECODER *decoder, int channel, WAVELET *wavelet, int index) +{ + PRESCALE prescale = decoder->codec.prescale_table[index]; + + // Is the current wavelet at a higher level than wavelet level one? + if (index > 0) + { + // Reconstruct the lowpass band in the lower wavelet + const int lowpass_index = index - 1; + WAVELET *lowpass; + int lowpass_width; + int lowpass_height; + + lowpass = decoder->transform[channel].wavelet[lowpass_index]; + assert(lowpass != NULL); + if (! (lowpass != NULL)) { + return CODEC_ERROR_UNEXPECTED; + } + + lowpass_width = lowpass->width; + lowpass_height = lowpass->height; + + // Check that the reconstructed wavelet is valid + if( lowpass_width <= 0 || lowpass_height <= 0 ) + { + assert(false); + return CODEC_ERROR_IMAGE_DIMENSIONS; + } + + // Check that the lowpass band has not already been reconstructed + assert((lowpass->valid_band_mask & BandValidMask(0)) == 0); + + // Check that all of the wavelet bands have been decoded + assert(BandsAllValid(wavelet)); + + // Decode the lowpass band in the wavelet one lower level than the input wavelet + TransformInverseSpatialQuantLowpass(decoder->allocator, wavelet, lowpass, prescale); + + // Update the band valid flags + UpdateWaveletValidBandMask(lowpass, 0); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Set the bit for the specified subband in the decoded band mask + The decoded subband mask is used to track which subbands have been + decoded in the current channel. It is reset at the start of each + channel. + */ +CODEC_ERROR SetDecodedBandMask(CODEC_STATE *codec, int subband) +{ + if (0 <= subband && subband < MAX_SUBBAND_COUNT) { + codec->decoded_subband_mask |= (1 << subband); + } + return CODEC_ERROR_OKAY; +} + +/*! + @brief Decoded the lowpass band from the bitstream + The wavelet at the highest level is passes as an argument. + This routine decodes lowpass band in the bitstream into the + lowpass band of the wavelet. + */ +CODEC_ERROR DecodeLowpassBand(DECODER *decoder, BITSTREAM *stream, WAVELET *wavelet) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + CODEC_STATE *codec = &decoder->codec; + + int lowpass_band_width; // Lowpass band dimensions + int lowpass_band_height; + int lowpass_band_pitch; + PIXEL *lowpass_band_ptr; // Pointer into the lowpass band + + PRECISION lowpass_precision; // Number of bits per lowpass coefficient + + int row, column; + + lowpass_band_width = wavelet->width; + lowpass_band_height = wavelet->height; + lowpass_band_pitch = wavelet->pitch/sizeof(PIXEL); + lowpass_band_ptr = wavelet->data[0]; + + lowpass_precision = codec->lowpass_precision; + + // Decode each row in the lowpass image + for (row = 0; row < lowpass_band_height; row++) + { + for (column = 0; column < lowpass_band_width; column++) + { + COEFFICIENT lowpass_value = (COEFFICIENT)GetBits(stream, lowpass_precision); + //assert(0 <= lowpass_value && lowpass_value <= COEFFICIENT_MAX); + + //if (lowpass_value > COEFFICIENT_MAX) { + // lowpass_value = COEFFICIENT_MAX; + //} + + lowpass_band_ptr[column] = lowpass_value; + } + + // Advance to the next row in the lowpass image + lowpass_band_ptr += lowpass_band_pitch; + } + // Align the bitstream to the next tag value pair + AlignBitsSegment(stream); + + // Return indication of lowpass decoding success + return error; +} + +/*! + @brief Decode the highpass band from the bitstream + The specified wavelet band is decoded from the bitstream + using the codebook and encoding method specified in the + bitstream. + */ +CODEC_ERROR DecodeHighpassBand(DECODER *decoder, BITSTREAM *stream, WAVELET *wavelet, int band) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Get the highpass band dimensions + DIMENSION width = wavelet->width; //codec->band.width; + DIMENSION height = wavelet->height; //codec->band.height; + + // Check that the band index is in range + assert(0 <= band && band < wavelet->band_count); + + // Encoded coefficients start on a tag boundary + AlignBitsSegment(stream); + + // Decode this subband + error = DecodeBandRuns(stream, decoder->codebook, wavelet->data[band], width, height, wavelet->pitch); + assert(error == CODEC_ERROR_OKAY); + + // Return failure if a problem was encountered while reading the band coefficients + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // The encoded band coefficients end on a bitstream word boundary + // to avoid interference with the marker for the coefficient band trailer + AlignBitsWord(stream); + + // Decode the band trailer + error = DecodeBandTrailer(stream); + decoder->error = error; + assert(error == CODEC_ERROR_OKAY); + return error; +} + +/*! + @brief Decode the highpass band from the bitstream + The highpass band in the bitstream is decoded using the specified + codebook. This routine assumes that the highpass band was encoded + using the run lengths encoding method which is the default for all + current codec implementations. + The encoded highpass band consists of signed values and runs of zeros. + Each codebook entry specifies either an unsigned magnitude with a run + length of one or a run of zeros. The unsigned magnitude is immediately + followed by the sign bit. + Unsigned magnitudes always have a run length of one. + Note that runs of zeros can straddle end of line boundaries. + The end of the highpass band is marked by a special codeword. + Special codewords in the codebook have a run length of zero. + The value indicates the type or purpose of the special codeword. + */ +CODEC_ERROR DecodeBandRuns(BITSTREAM *stream, CODEBOOK *codebook, PIXEL *data, + DIMENSION width, DIMENSION height, DIMENSION pitch) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + size_t data_count; + size_t row_padding; + int row = 0; + int column = 0; + int index = 0; + //BITWORD special; + RUN run = RUN_INITIALIZER; + + // Convert the pitch to units of pixels + pitch /= sizeof(PIXEL); + + // Check that the band dimensions are reasonable + assert(width <= pitch); + + // Compute the number of pixels encoded into the band + data_count = height * width; + row_padding = pitch - width; + + while (data_count > 0) + { + // Get the next run length and value + error = GetRun(stream, codebook, &run); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Check that the run does not extend past the end of the band + assert(run.count <= data_count); + + // Copy the value into the specified number of pixels in the band + while (run.count > 0) + { + // Reached the end of the column? + if (column == width) + { + // Need to pad the end of the row? + if (row_padding > 0) + { + int count; + for (count = 0; (size_t)count < row_padding; count++) { + data[index++] = 0; + } + } + + // Advance to the next row + row++; + column = 0; + } + + data[index++] = (PIXEL)run.value; + column++; + run.count--; + data_count--; + } + } + + // The last run should have ended at the end of the band + assert(data_count == 0 && run.count == 0); + + // Check for the special codeword that marks the end of the highpass band + error = GetRlv(stream, codebook, &run); + if (error == CODEC_ERROR_OKAY) { + if (! (run.count == 0 || run.value == SPECIAL_MARKER_BAND_END)) { + error = CODEC_ERROR_BAND_END_MARKER; + } + } + + return error; +} + +/*! + @brief Decode the band trailer that follows a highpass band + This routine aligns the bitstream to a tag value boundary. + Currently the band trailer does not perform any function beyond + preparing the bitstream for reading the next tag value pair. + */ +CODEC_ERROR DecodeBandTrailer(BITSTREAM *stream) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Advance the bitstream to a tag boundary + AlignBitsSegment(stream); + + return error; +} + +/*! + @brief Return true if the bitstream has been completely decoded + The end of sample flag is set to true when enough of the sample has been read + from the bitstream to allow the output frame to be fully reconstructed. Any + remaining bits in the sample can be ignored and it may be the case that further + reads from the bitstream will result in an error. + The end of sample flag is set when the tag for the frame trailer is found, but + may be set when sufficient subbands have been decoded to allow the frame to be + reconstructed at the desired resolution. For example, it is not an error if + bands at level one in the wavelet tree are not present in the bitstream when + decoding to half resolution. The decoder should set the end of sample flag as + soon as it is no longer necessary to read further information from the sample. + + @todo Rename this routine to end of image or end of bitstream? + */ +bool EndOfSample(DECODER *decoder) +{ + return decoder->codec.end_of_sample; +} + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) +/*! + @brief Return true of the layer has been completely read from the bitstream + */ +bool EndOfLayer(DECODER *decoder) +{ + return (decoder->codec.end_of_layer || decoder->codec.end_of_sample); +} +#endif + +/*! + @brief Return true if the entire bitstream header has been decoded + The bitstream header has been completely decoded when at least one + non-header parameter has been encountered in the bitstream and all + of the required header parameters have been decoded. + + @todo Create a bitstream that can be used to test this predicate. + */ +bool IsHeaderComplete(DECODER *decoder) +{ + return (decoder->header_finished && + ((decoder->header_mask & BITSTREAM_HEADER_FLAGS_REQUIRED) == BITSTREAM_HEADER_FLAGS_REQUIRED)); +} + +/*! + @brief Return true if all channels in the bitstream have been processed + It is only necessary to test the bands in the largest wavelet in each + channel since its lowpass band would not be finished if the wavelets + at the higher levels were incomplete. + */ +bool IsDecodingComplete(DECODER *decoder) +{ + int channel_count = decoder->codec.channel_count; + int channel_index; + + for (channel_index = 0; channel_index < channel_count; channel_index++) + { + WAVELET *wavelet = decoder->transform[channel_index].wavelet[0]; + + // Processing is not complete if the wavelet has not been allocated + if (wavelet == NULL) return false; + + // Processing is not complete unless all bands have been processed + if (!AllBandsValid(wavelet)) return false; + } + + // All bands in all wavelets in all channels are done + return true; +} + +/*! + @brief Perform the final wavelet transform in each channel to compute the component arrays + Each channel is decoded and the lowpass and highpass bands are used to reconstruct the + lowpass band in the wavelet at the next lower level by applying the inverse wavelet filter. + Highpass band decoding and computation of the inverse wavelet transform in each channel + stops when the wavelet at the level immediately above the output frame is computed. + This routine performs the final wavelet transform in each channel and combines the channels + into a single output frame. Note that this routine is called for each layer in a sample, + producing an output frame for each layer. The output frames for each layer must be combine + by an image compositing operation into a single output frame for the fully decoded sample. + */ +CODEC_ERROR ReconstructUnpackedImage(DECODER *decoder, UNPACKED_IMAGE *image) +{ + TIMESTAMP("[BEG]", 2) + + CODEC_ERROR error = CODEC_ERROR_OKAY; + gpr_allocator *allocator = decoder->allocator; + + int channel_count = decoder->codec.channel_count; + int channel_number; + + // Check for enough space in the local array allocations + //assert(channel_count <= MAX_CHANNEL_COUNT); + + // Allocate the vector of component arrays + size_t size = channel_count * sizeof(COMPONENT_ARRAY); + image->component_array_list = allocator->Alloc(size); + if (image->component_array_list == NULL) { + return CODEC_ERROR_OUTOFMEMORY; + } + + // Clear the component array information so that the state is consistent + image->component_count = 0; + memset(image->component_array_list, 0, size); + + for (channel_number = 0; channel_number < channel_count; channel_number++) + { + // Get the dimensions of this channel + DIMENSION channel_width = decoder->channel[channel_number].width; + DIMENSION channel_height = decoder->channel[channel_number].height; + PRECISION bits_per_component = decoder->channel[channel_number].bits_per_component; + + // Amount of prescaling applied to the component array values before encoding + PRESCALE prescale = decoder->codec.prescale_table[0]; + + // Allocate the component array for this channel + error = AllocateComponentArray(allocator, + &image->component_array_list[channel_number], + channel_width, + channel_height, + bits_per_component); + + if (error != CODEC_ERROR_OKAY) { + return error; + } + + error = TransformInverseSpatialQuantArray(allocator, + decoder->transform[channel_number].wavelet[0], + image->component_array_list[channel_number].data, + channel_width, + channel_height, + image->component_array_list[channel_number].pitch, + prescale); + if (error != CODEC_ERROR_OKAY) { + return error; + } + } + + // One component array is output by the decoding process per channel in the bitstream + image->component_count = channel_count; + + TIMESTAMP("[END]", 2) + + return error; +} + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) +/*! + @brief Perform the final wavelet transform in each channel to compute the output frame + Each channel is decoded and the lowpass and highpass bands are used to reconstruct the + lowpass band in the wavelet at the next lower level by applying the inverse wavelet filter. + Highpass band decoding and computation of the inverse wavelet transform in each channel + stops when the wavelet at the level immediately above the output frame is computed. + This routine performs the final wavelet transform in each channel and combines the channels + into a single output frame. Note that this routine is called for each layer in a sample, + producing an output frame for each layer. The output frames for each layer must be combine + by an image compositing operation into a single output frame for the fully decoded sample. + Refer to @ref ReconstructSampleFrame for the details of how the frames from each layer + are combined to produce the output frame for the decoded sample. + */ +CODEC_ERROR ReconstructLayerImage(DECODER *decoder, IMAGE *image) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + DIMENSION decoded_width = decoder->decoded.width; + DIMENSION decoded_height = decoder->decoded.height; + int channel_count = decoder->codec.channel_count; + + //DIMENSION layer_width = LayerWidth(decoder, decoded_width); + //DIMENSION layer_height = LayerHeight(decoder, decoded_height); + DIMENSION layer_width = decoded_width; + DIMENSION layer_height = decoded_height; + + //TODO: Adjust the layer width to account for chroma sampling + + // Allocate a buffer for the intermediate output from each wavelet transform + size_t decoded_frame_pitch = layer_width * channel_count * sizeof(PIXEL); + size_t decoded_frame_size = layer_height * decoded_frame_pitch; + PIXEL *decoded_frame_buffer = (PIXEL *)Alloc(decoder->allocator, decoded_frame_size); + if (decoded_frame_buffer == NULL) { + return CODEC_ERROR_OUTOFMEMORY; + } + + error = TransformInverseSpatialQuantBuffer(decoder, decoded_frame_buffer, (DIMENSION)decoded_frame_pitch); + if (error == CODEC_ERROR_OKAY) + { + // Pack the decoded frame into the output format + error = PackOutputImage(decoded_frame_buffer, decoded_frame_pitch, decoder->encoded.format, image); + } + + // Free the buffer for the decoded frame + Free(decoder->allocator, decoded_frame_buffer); + + return error; +} +#endif + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) +/*! + @brief Combine multiple decoded frames from each layer into a single output frame + An encoded sample may contain multiple sub-samples called layers. For example, + there may be two sub-samples (layers) for the left and right frames in a stereo pair. + Note that the baseline profile supports only one layer per sample. + Each layer is decoded independently to produce an output frame for that layer. The + CineForm codec does not support dependent sub-samples in any of the existing profiles. + + This routine forms a composite frame for the output of the completely decoded sample + from the individual frames obtained by decoding each layer. It is contemplated that + any image compositing algorithm could be used to combine decoded layers, although the + most sophisticated algorithms might be reserved for the most advanced profiles. + The dimensions of the output frame could be much larger than the dimensions of any + of the frames decoded from individual layers. Compositing could overlay the frames + from the individual layers with an arbitrary spatial offset applied to the frame from + each layer, creating a collage from frames decoded from the individual layers. Typical + applications may use only the most elementary compositing operations. + */ +CODEC_ERROR ReconstructSampleFrame(DECODER *decoder, IMAGE image_array[], int frame_count, IMAGE *output_image) +{ + DIMENSION frame_width = image_array[0].width; + DIMENSION field_height = image_array[0].height; + DIMENSION frame_height = 2 * field_height; + PIXEL_FORMAT frame_format = image_array[0].format; + + AllocImage(decoder->allocator, output_image, frame_width, frame_height, frame_format); + + return ComposeFields(image_array, frame_count, output_image); +} +#endif + + diff --git a/source/lib/vc5_decoder/decoder.h b/source/lib/vc5_decoder/decoder.h new file mode 100755 index 0000000..013aa31 --- /dev/null +++ b/source/lib/vc5_decoder/decoder.h @@ -0,0 +1,336 @@ +/*! @file decoder.h + * + * @brief Core decoder functions and data structure + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DECODER_H +#define DECODER_H + +/*! + Data structure for the buffers and information used by + the decoder. + + The decoder data structure contains information that will be + used by the decoder for decoding every sample in the sequence. + Information that varies during decoding, such as the current + subband index or the dimensions of the bands in the wavelet that + is being decoded, is stored in the codec state. + + @todo Consider changing the transform data structure to use a + vector of wavelets rather than a vector of wavelet pointers. + + @todo Remove unused substructures + + @todo Dynamically allocate the vector of wavelet trees based on the + actual number of channels rather than the maximum channel count. + + @todo Need to handle the cases where header parameters are provided + by the application instead of being in the bitstream. + */ +typedef struct _decoder +{ + CODEC_ERROR error; //!< Error code from the most recent codec operation + gpr_allocator *allocator; //!< Memory allocator used to allocate all dyynamic data + CODEC_STATE codec; //!< Information gathered while decoding the current sample + + //! Parts of the VC-5 standard that are supported at runtime by the codec implementation + ENABLED_PARTS enabled_parts; + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + uint64_t frame_number; //!< Every sample in a clip has a unique frame number +#endif + + uint16_t header_mask; //!< Track which header parameters have been decoded + bool header_finished; //!< Finished decoding the bitstream header? + bool memory_allocated; //!< True if memory for decoding has been allocated + + //! Dimensions of each channel found in the bitstream + struct _channel + { + DIMENSION width; //!< Width of this channel + DIMENSION height; //!< Height of this channnel + + //! Bits per component for the component array corresponding to this channel + uint_least8_t bits_per_component; + + bool initialized; //!< Has the channel information been initialized? + + bool found_first_codeblock; //!< Has the first codeblock in the channel been found? + + } channel[MAX_CHANNEL_COUNT]; //!< Information about each channel in the bitstream + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + //! Dimensions and format of the encoded image + struct _encoded + { + DIMENSION width; //!< Encoded width + DIMENSION height; //!< Encoded height + IMAGE_FORMAT format; //!< Encoded format + + } encoded; //!< Information about the image as represented in the bitstream +#endif + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + //! Dimensions and format of the decoded image + struct _decoded + { + DIMENSION width; //!< Decoded width + DIMENSION height; //!< Decoded height + //RESOLUTION resolution; + + } decoded; //!< Information about the decoded component arrays +#endif + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + //! Dimensions and format of the frame after post-processing (see @ref ImageRepackingProcess) + struct _output + { + DIMENSION width; //!< Output frame width + DIMENSION height; //!< Output frame height + PIXEL_FORMAT format; //!< Output frame pixel format + + } output; //!< Information about the packed image output by the image repacking process +#endif + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + //! Dimensions and format of the image output by the display process + struct _display + { + DIMENSION width; //!< Output frame width + DIMENSION height; //!< Output frame height + PIXEL_FORMAT format; //!< Output frame pixel format + + } display; //!< Information about the displayable image output by the display process +#endif + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + int layer_count; //!< Number of subsamples in each sample +#endif + + int wavelet_count; //!< Number of wavelets in each channel + + int subbands_to_decode; + + //! Wavelet tree for each channel + TRANSFORM transform[MAX_CHANNEL_COUNT]; + + //! Pointer to the active codebook for variable-length codes + CODEBOOK *codebook; + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + uint8_t image_sequence_identifier[16]; //!< UUID for the unique image sequence identifier + uint32_t image_sequence_number; //!< Number of the image in the image sequence +#endif + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + bool progressive; //!< True if the encoded frame is progressive + bool top_field_first; //!< True if the top field is encoded first +#endif + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + bool section_flag; //!< Control whether section processing is enabled + FILE *section_logfile; //!< Log file for writing section information +#endif + +} DECODER; + +/*! + @brief Information that can be obtained from an bitstream header + + The bitstream header consists of tag-value pairs that must occur in the bitstream + before the first codeblock if the parameters are present in the bitstream. + + Consider organizing the values obtained from the bitstream into input parameters + and encoded parameters, as is done elsewhere in the decoder, even though the + bitstream does not have such a rigid syntax. + */ +typedef struct _bitstream_header +{ + uint16_t channel_count; //!< Number of channels in the bitstream + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + uint64_t frame_number; //!< Every sample in a clip has a unique frame number + PIXEL_FORMAT input_format; //!< Pixel format of the frame input to the encoder + + // Encoded dimensions and format of the encoded frame (including padding) + DIMENSION encoded_width; //!< Width of the encoded frame + DIMENSION encoded_height; //!< Height of the encoded frame + + IMAGE_FORMAT encoded_format; //!< Encoded format + + // The display aperture within the encoded frame + DIMENSION row_offset; + DIMENSION column_offset; + DIMENSION display_width; //!< Width of the displayable frame (if specified in the sample) + DIMENSION display_height; //!< Height of the displayable frame (if specified in the sample) +#endif +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + DIMENSION video_channel_count; // Number of layers? + DIMENSION current_video_channel; //TODO: Find better way to handle this + int layer_count; //!< Number of layers in the sample + bool progressive; //!< Progressive versus interlaced frames + bool top_field_first; //!< Interlaced frame with top field first +#endif + +} BITSTREAM_HEADER; + +//! Flags that indicate which header parameters have been assigned values +typedef enum _bitstream_header_flags +{ + BITSTREAM_HEADER_FLAGS_IMAGE_WIDTH = (1 << 0), + BITSTREAM_HEADER_FLAGS_IMAGE_HEIGHT = (1 << 1), + BITSTREAM_HEADER_FLAGS_CHANNEL_COUNT = (1 << 2), + BITSTREAM_HEADER_FLAGS_SUBBAND_COUNT = (1 << 3), + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + BITSTREAM_HEADER_FLAGS_IMAGE_FORMAT = (1 << 4), + BITSTREAM_HEADER_FLAGS_PATTERN_WIDTH = (1 << 5), + BITSTREAM_HEADER_FLAGS_PATTERN_HEIGHT = (1 << 6), + BITSTREAM_HEADER_FLAGS_COMPONENTS_PER_SAMPLE = (1 << 7), + BITSTREAM_HEADER_FLAGS_MAX_BITS_PER_COMPONENT = (1 << 8), + + //! Required header parameters + BITSTREAM_HEADER_FLAGS_REQUIRED = (BITSTREAM_HEADER_FLAGS_IMAGE_WIDTH | + BITSTREAM_HEADER_FLAGS_IMAGE_HEIGHT | + BITSTREAM_HEADER_FLAGS_IMAGE_FORMAT | + BITSTREAM_HEADER_FLAGS_PATTERN_WIDTH | + BITSTREAM_HEADER_FLAGS_PATTERN_HEIGHT | + BITSTREAM_HEADER_FLAGS_COMPONENTS_PER_SAMPLE), +#else + + //! Required header parameters + BITSTREAM_HEADER_FLAGS_REQUIRED = (BITSTREAM_HEADER_FLAGS_IMAGE_WIDTH | + BITSTREAM_HEADER_FLAGS_IMAGE_HEIGHT), +#endif + +} BITSTREAM_HEADER_FLAGS; + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR InitDecoder(DECODER *decoder, const gpr_allocator *allocator); + + CODEC_ERROR SetDecoderLogfile(DECODER *decoder, FILE *logfile); + + CODEC_ERROR ReleaseDecoder(DECODER *decoder); + + CODEC_ERROR PrepareDecoderState(DECODER *decoder, const DECODER_PARAMETERS *parameters); + + CODEC_ERROR PrepareDecoderTransforms(DECODER *decoder); + + CODEC_ERROR SetOutputImageFormat(DECODER *decoder, + const DECODER_PARAMETERS *parameters, + DIMENSION *width_out, + DIMENSION *height_out, + PIXEL_FORMAT *format_out); + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + CODEC_ERROR SetDisplayImageFormat(DECODER *decoder, + const DECODER_PARAMETERS *parameters, + DIMENSION *width_out, + DIMENSION *height_out, + PIXEL_FORMAT *format_out); +#endif + + bool ChannelLowpassBandsAllValid(const DECODER *decoder, int wavelet_index); + + PIXEL_FORMAT EncodedPixelFormat(const DECODER *decoder, const DECODER_PARAMETERS *parameters); + + CODEC_ERROR PackOutputImage(void *buffer, size_t pitch, int encoded_format, IMAGE *image); + + CODEC_ERROR ImageRepackingProcess(const UNPACKED_IMAGE *unpacked_image, + PACKED_IMAGE *packed_image, + const DECODER_PARAMETERS *parameters); + + CODEC_ERROR UpdateCodecState(DECODER *decoder, BITSTREAM *stream, TAGVALUE segment); + + bool IsHeaderParameter(TAGWORD tag); + + CODEC_ERROR UpdateHeaderParameter(DECODER *decoder, TAGWORD tag); + + CODEC_ERROR PrepareDecoder(DECODER *decoder, const DECODER_PARAMETERS *parameters); + + CODEC_ERROR AllocDecoderTransforms(DECODER *decoder); + + CODEC_ERROR ReleaseDecoderTransforms(DECODER *decoder); + + CODEC_ERROR AllocDecoderBuffers(DECODER *decoder); + + CODEC_ERROR ReleaseDecoderBuffers(DECODER *decoder); + + CODEC_ERROR AllocateChannelWavelets(DECODER *decoder, int channel); + + CODEC_ERROR DecodeStream(STREAM *stream, UNPACKED_IMAGE *image, const DECODER_PARAMETERS *parameters); + + CODEC_ERROR DecodeImage(STREAM *stream, IMAGE *image, RGB_IMAGE *rgb_image, DECODER_PARAMETERS *parameters); + + CODEC_ERROR DecodingProcess(DECODER *decoder, BITSTREAM *stream, UNPACKED_IMAGE *image, const DECODER_PARAMETERS *parameters); + + CODEC_ERROR DecodeSingleImage(DECODER *decoder, BITSTREAM *input, UNPACKED_IMAGE *image, const DECODER_PARAMETERS *parameters); + + CODEC_ERROR DecodeSampleLayer(DECODER *decoder, BITSTREAM *input, IMAGE *image); + + CODEC_ERROR DecodeChannelSubband(DECODER *decoder, BITSTREAM *input, size_t chunk_size); + + CODEC_ERROR ReconstructWaveletBand(DECODER *decoder, int channel, WAVELET *wavelet, int index); + + CODEC_ERROR ParseChannelIndex(BITSTREAM *stream, uint32_t *channel_size, int channel_count); + + DIMENSION LayerWidth(DECODER *decoder, DIMENSION width); + DIMENSION LayerHeight(DECODER *decoder, DIMENSION height); + + CODEC_ERROR ProcessSampleMarker(DECODER *decoder, BITSTREAM *stream, TAGWORD marker); + + CODEC_ERROR SetDecodedBandMask(CODEC_STATE *codec, int subband); + + CODEC_ERROR DecodeLowpassBand(DECODER *decoder, BITSTREAM *stream, WAVELET *wavelet); + + CODEC_ERROR DecodeHighpassBand(DECODER *decoder, BITSTREAM *stream, WAVELET *wavelet, int band); + + CODEC_ERROR DecodeBandRuns(BITSTREAM *stream, CODEBOOK *codebook, PIXEL *data, + DIMENSION width, DIMENSION height, DIMENSION pitch); + + CODEC_ERROR DecodeBandTrailer(BITSTREAM *stream); + + CODEC_ERROR DecodeSampleChannelHeader(DECODER *decoder, BITSTREAM *stream); + + bool IsHeaderComplete(DECODER *decoder); + + bool EndOfSample(DECODER *decoder); + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + bool EndOfLayer(DECODER *decoder); + bool IsLayerComplete(DECODER *decoder); +#endif + + bool IsDecodingComplete(DECODER *decoder); + + CODEC_ERROR ReconstructUnpackedImage(DECODER *decoder, UNPACKED_IMAGE *image); + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + CODEC_ERROR ReconstructLayerImage(DECODER *decoder, IMAGE *image); +#endif + + CODEC_ERROR TransformInverseSpatialQuantBuffer(DECODER *decoder, void *output_buffer, DIMENSION output_width, DIMENSION output_pitch); + +#ifdef __cplusplus +} +#endif + +#endif // DECODER_H diff --git a/source/lib/vc5_decoder/dequantize.c b/source/lib/vc5_decoder/dequantize.c new file mode 100755 index 0000000..48cfb64 --- /dev/null +++ b/source/lib/vc5_decoder/dequantize.c @@ -0,0 +1,88 @@ +/*! @file dequantize.c + * + * @brief Implementation of inverse quantization functions + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +// Not using midpoint correction in dequantization +static const int midpoint = 0; + +/*! + @brief Dequantize a band with the specified dimensions + + The companding curve is inverted and the value is multiplied by the + quantization value that was used by the encoder to compress the band. +*/ +CODEC_ERROR DequantizeBandRow16s(PIXEL *input, int width, int quantization, PIXEL *output) +{ + int column; + + // Undo quantization in the entire row + for (column = 0; column < width; column++) + { + int32_t value = input[column]; + + // Invert the companding curve (if any) + value = UncompandedValue(value); + + // Dequantize the absolute value + if (value > 0) + { + value = (quantization * value) + midpoint; + } + else if (value < 0) + { + value = neg(value); + value = (quantization * value) + midpoint; + value = neg(value); + } + + // Store the dequantized coefficient + output[column] = ClampPixel(value); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief This function dequantizes the pixel value + + The inverse companding curve is applied to convert the pixel value + to its quantized value and then the pixel value is multiplied by + the quantization parameter. +*/ +PIXEL DequantizedValue(int32_t value, int quantization) +{ + // Invert the companding curve (if any) + value = UncompandedValue(value); + + // Dequantize the absolute value + if (value > 0) + { + value = (quantization * value) + midpoint; + } + else if (value < 0) + { + value = neg(value); + value = (quantization * value) + midpoint; + value = neg(value); + } + + return ClampPixel(value); +} diff --git a/source/lib/vc5_decoder/dequantize.h b/source/lib/vc5_decoder/dequantize.h new file mode 100755 index 0000000..748bc36 --- /dev/null +++ b/source/lib/vc5_decoder/dequantize.h @@ -0,0 +1,36 @@ +/*! @file dequantize.h + * + * @brief Declaration of inverse quantization functions + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef QUANTIZE_H +#define QUANTIZE_H + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR DequantizeBandRow16s(PIXEL *input, int width, int quantization, PIXEL *output); + + PIXEL DequantizedValue(int32_t value, int quantization); + +#ifdef __cplusplus +} +#endif + +#endif // QUANTIZE_H diff --git a/source/lib/vc5_decoder/headers.h b/source/lib/vc5_decoder/headers.h new file mode 100755 index 0000000..54e17e4 --- /dev/null +++ b/source/lib/vc5_decoder/headers.h @@ -0,0 +1,49 @@ +/*! @file headers.h + * + * @brief This file includes all of the header files that are used by the decoder. + * + * Note that some header files are only used by the main program that + * calls the codec or are only used for debugging are not included by this file. + * Only headers that are part of the reference decoder are included by this file. + + * Including a single header file in all reference decoder source files + * ensures that all modules see the same header files in the same order. + + * This file can be used for creating a pre-compiled header if the + * compiler supports that capabilities. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HEADERS_H +#define HEADERS_H + +#include "common.h" + +#include "vlc.h" +#include "raw.h" +#include "bitstream.h" +#include "dequantize.h" +#include "parameters.h" +#include "inverse.h" +#include "codebooks.h" +#include "wavelet.h" +#include "syntax.h" +#include "decoder.h" +#include "component.h" +#include "vc5_decoder.h" + +#endif // HEADERS_H diff --git a/source/lib/vc5_decoder/inverse.c b/source/lib/vc5_decoder/inverse.c new file mode 100755 index 0000000..cacc7be --- /dev/null +++ b/source/lib/vc5_decoder/inverse.c @@ -0,0 +1,1188 @@ +/*! @file inverse.c + * + * @brief Implementation of the inverse wavelet transforms. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +//! Rounding adjustment used by the inverse wavelet transforms +static const int32_t rounding = 4; + +/*! + @brief Apply the inverse horizontal wavelet transform + This routine applies the inverse wavelet transform to a row of + lowpass and highpass coefficients, producing an output row that + is write as wide. + */ +STATIC CODEC_ERROR InvertHorizontal16s(PIXEL *lowpass, //!< Horizontal lowpass coefficients + PIXEL *highpass, //!< Horizontal highpass coefficients + PIXEL *output, //!< Row of reconstructed results + DIMENSION input_width, //!< Number of values in the input row + DIMENSION output_width //!< Number of values in the output row +) +{ + const int last_column = input_width - 1; + + int32_t even; + int32_t odd; + + // Start processing at the beginning of the row + int column = 0; + + // Process the first two output points with special filters for the left border + even = 0; + odd = 0; + + // Apply the even reconstruction filter to the lowpass band + even += 11 * lowpass[column + 0]; + even -= 4 * lowpass[column + 1]; + even += 1 * lowpass[column + 2]; + even += rounding; + even = DivideByShift(even, 3); + + // Add the highpass correction + even += highpass[column]; + even >>= 1; + + // The lowpass result should be a positive number + //assert(0 <= even && even <= INT16_MAX); + + // Apply the odd reconstruction filter to the lowpass band + odd += 5 * lowpass[column + 0]; + odd += 4 * lowpass[column + 1]; + odd -= 1 * lowpass[column + 2]; + odd += rounding; + odd = DivideByShift(odd, 3); + + // Subtract the highpass correction + odd -= highpass[column]; + odd >>= 1; + + // The lowpass result should be a positive number + //assert(0 <= odd && odd <= INT16_MAX); + + // Store the last two output points produced by the loop + output[2 * column + 0] = clamp_uint14(even); + output[2 * column + 1] = clamp_uint14(odd); + + // Advance to the next input column (second pair of output values) + column++; + + // Process the rest of the columns up to the last column in the row + for (; column < last_column; column++) + { + int32_t even = 0; // Result of convolution with even filter + int32_t odd = 0; // Result of convolution with odd filter + + // Apply the even reconstruction filter to the lowpass band + + even += lowpass[column - 1]; + even -= lowpass[column + 1]; + even += 4; + even >>= 3; + even += lowpass[column + 0]; + + // Add the highpass correction + even += highpass[column]; + even >>= 1; + + // The lowpass result should be a positive number + //assert(0 <= even && even <= INT16_MAX); + + // Place the even result in the even column + //output[2 * column + 0] = clamp_uint12(even); + output[2 * column + 0] = clamp_uint14(even); + + // Apply the odd reconstruction filter to the lowpass band + odd -= lowpass[column - 1]; + odd += lowpass[column + 1]; + odd += 4; + odd >>= 3; + odd += lowpass[column + 0]; + + // Subtract the highpass correction + odd -= highpass[column]; + odd >>= 1; + + // The lowpass result should be a positive number + //assert(0 <= odd && odd <= INT16_MAX); + + // Place the odd result in the odd column + //output[2 * column + 1] = clamp_uint14(odd); + output[2 * column + 1] = clamp_uint14(odd); + } + + // Should have exited the loop at the column for right border processing + assert(column == last_column); + + // Process the last two output points with special filters for the right border + even = 0; + odd = 0; + + // Apply the even reconstruction filter to the lowpass band + even += 5 * lowpass[column + 0]; + even += 4 * lowpass[column - 1]; + even -= 1 * lowpass[column - 2]; + even += rounding; + even = DivideByShift(even, 3); + + // Add the highpass correction + even += highpass[column]; + even >>= 1; + + // The lowpass result should be a positive number + //assert(0 <= even && even <= INT16_MAX); + + // Place the even result in the even column + output[2 * column + 0] = clamp_uint14(even); + + if (2 * column + 1 < output_width) + { + // Apply the odd reconstruction filter to the lowpass band + odd += 11 * lowpass[column + 0]; + odd -= 4 * lowpass[column - 1]; + odd += 1 * lowpass[column - 2]; + odd += rounding; + odd = DivideByShift(odd, 3); + + // Subtract the highpass correction + odd -= highpass[column]; + odd >>= 1; + + // The lowpass result should be a positive number + //assert(0 <= odd && odd <= INT16_MAX); + + // Place the odd result in the odd column + output[2 * column + 1] = clamp_uint14(odd); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Apply the inverse horizontal wavelet transform + This routine is similar to @ref InvertHorizontal16s, but a scale factor + that was applied during encoding is removed from the output values. + */ +STATIC CODEC_ERROR InvertHorizontalDescale16s(PIXEL *lowpass, PIXEL *highpass, PIXEL *output, + DIMENSION input_width, DIMENSION output_width, + int descale) +{ + const int last_column = input_width - 1; + + // Start processing at the beginning of the row + int column = 0; + + int descale_shift = 0; + + int32_t even; + int32_t odd; + + /* + The implementation of the inverse filter includes descaling by a factor of two + because the last division by two in the computation of the even and odd results + that is performed using a right arithmetic shift has been omitted from the code. + */ + if (descale == 2) { + descale_shift = 1; + } + + // Check that the descaling value is reasonable + assert(descale_shift >= 0); + + // Process the first two output points with special filters for the left border + even = 0; + odd = 0; + + // Apply the even reconstruction filter to the lowpass band + even += 11 * lowpass[column + 0]; + even -= 4 * lowpass[column + 1]; + even += 1 * lowpass[column + 2]; + even += rounding; + even = DivideByShift(even, 3); + + // Add the highpass correction + even += highpass[column]; + + // Remove any scaling used during encoding + even <<= descale_shift; + + // The lowpass result should be a positive number + //assert(0 <= even && even <= INT16_MAX); + + // Apply the odd reconstruction filter to the lowpass band + odd += 5 * lowpass[column + 0]; + odd += 4 * lowpass[column + 1]; + odd -= 1 * lowpass[column + 2]; + odd += rounding; + odd = DivideByShift(odd, 3); + + // Subtract the highpass correction + odd -= highpass[column]; + + // Remove any scaling used during encoding + odd <<= descale_shift; + + // The lowpass result should be a positive number + //assert(0 <= odd && odd <= INT16_MAX); + + output[2 * column + 0] = ClampPixel(even); + output[2 * column + 1] = ClampPixel(odd); + + // Advance to the next input column (second pair of output values) + column++; + + // Process the rest of the columns up to the last column in the row + for (; column < last_column; column++) + { + int32_t even = 0; // Result of convolution with even filter + int32_t odd = 0; // Result of convolution with odd filter + + // Apply the even reconstruction filter to the lowpass band + even += lowpass[column - 1]; + even -= lowpass[column + 1]; + even += 4; + even >>= 3; + even += lowpass[column + 0]; + + // Add the highpass correction + even += highpass[column]; + + // Remove any scaling used during encoding + even <<= descale_shift; + + // The lowpass result should be a positive number + //assert(0 <= even && even <= INT16_MAX); + + // Place the even result in the even column + output[2 * column + 0] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd -= lowpass[column - 1]; + odd += lowpass[column + 1]; + odd += 4; + odd >>= 3; + odd += lowpass[column + 0]; + + // Subtract the highpass correction + odd -= highpass[column]; + + // Remove any scaling used during encoding + odd <<= descale_shift; + + // The lowpass result should be a positive number + //assert(0 <= odd && odd <= INT16_MAX); + + // Place the odd result in the odd column + output[2 * column + 1] = ClampPixel(odd); + } + + // Should have exited the loop at the column for right border processing + assert(column == last_column); + + // Process the last two output points with special filters for the right border + even = 0; + odd = 0; + + // Apply the even reconstruction filter to the lowpass band + even += 5 * lowpass[column + 0]; + even += 4 * lowpass[column - 1]; + even -= 1 * lowpass[column - 2]; + even += rounding; + even = DivideByShift(even, 3); + + // Add the highpass correction + even += highpass[column]; + + // Remove any scaling used during encoding + even <<= descale_shift; + + // The lowpass result should be a positive number + //assert(0 <= even && even <= INT16_MAX); + + // Place the even result in the even column + output[2 * column + 0] = ClampPixel(even); + + if (2 * column + 1 < output_width) + { + // Apply the odd reconstruction filter to the lowpass band + odd += 11 * lowpass[column + 0]; + odd -= 4 * lowpass[column - 1]; + odd += 1 * lowpass[column - 2]; + odd += rounding; + odd = DivideByShift(odd, 3); + + // Subtract the highpass correction + odd -= highpass[column]; + + // Remove any scaling used during encoding + odd <<= descale_shift; + + // The lowpass result should be a positive number + //assert(0 <= odd && odd <= INT16_MAX); + + // Place the odd result in the odd column + output[2 * column + 1] = ClampPixel(odd); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Apply the inverse spatial wavelet filter + Dequantize the coefficients in the highpass bands and apply the + inverse spatial wavelet filter to compute a lowpass band that + has twice the width and height of the input bands. + The inverse vertical filter is applied to the upper and lower bands + on the left and the upper and lower bands on the right. The inverse + horizontal filter is applied to the left and right (lowpass and highpass) + results from the vertical inverse. Each application of the inverse + vertical filter produces two output rows and each application of the + inverse horizontal filter produces an output row that is twice as wide. + The inverse wavelet filter is a three tap filter. + + For the even output values, add and subtract the off-center values, + add the rounding correction, and divide by eight, then add the center + value, add the highpass coefficient, and divide by two. + + For the odd output values, the add and subtract operations for the + off-center values are reversed the the highpass coefficient is subtracted. + Divisions are implemented by right arithmetic shifts. + Special formulas for the inverse vertical filter are applied to the top + and bottom rows. + */ +CODEC_ERROR InvertSpatialQuant16s(gpr_allocator *allocator, + PIXEL *lowlow_band, int lowlow_pitch, + PIXEL *lowhigh_band, int lowhigh_pitch, + PIXEL *highlow_band, int highlow_pitch, + PIXEL *highhigh_band, int highhigh_pitch, + PIXEL *output_image, int output_pitch, + DIMENSION input_width, DIMENSION input_height, + DIMENSION output_width, DIMENSION output_height, + QUANT quantization[]) +{ + PIXEL *lowlow = (PIXEL *)lowlow_band; + PIXEL *lowhigh = lowhigh_band; + PIXEL *highlow = highlow_band; + PIXEL *highhigh = highhigh_band; + PIXEL *output = output_image; + PIXEL *even_lowpass; + PIXEL *even_highpass; + PIXEL *odd_lowpass; + PIXEL *odd_highpass; + PIXEL *even_output; + PIXEL *odd_output; + size_t buffer_row_size; + int last_row = input_height - 1; + int row, column; + + PIXEL *lowhigh_row[3]; + + PIXEL *lowhigh_line[3]; + PIXEL *highlow_line; + PIXEL *highhigh_line; + + QUANT highlow_quantization = quantization[HL_BAND]; + QUANT lowhigh_quantization = quantization[LH_BAND]; + QUANT highhigh_quantization = quantization[HH_BAND]; + + // Compute positions within the temporary buffer for each row of horizontal lowpass + // and highpass intermediate coefficients computed by the vertical inverse transform + buffer_row_size = input_width * sizeof(PIXEL); + + // Compute the positions of the even and odd rows of coefficients + even_lowpass = (PIXEL *)allocator->Alloc(buffer_row_size); + even_highpass = (PIXEL *)allocator->Alloc(buffer_row_size); + odd_lowpass = (PIXEL *)allocator->Alloc(buffer_row_size); + odd_highpass = (PIXEL *)allocator->Alloc(buffer_row_size); + + // Compute the positions of the dequantized highpass rows + lowhigh_line[0] = (PIXEL *)allocator->Alloc(buffer_row_size); + lowhigh_line[1] = (PIXEL *)allocator->Alloc(buffer_row_size); + lowhigh_line[2] = (PIXEL *)allocator->Alloc(buffer_row_size); + highlow_line = (PIXEL *)allocator->Alloc(buffer_row_size); + highhigh_line = (PIXEL *)allocator->Alloc(buffer_row_size); + + // Convert pitch from bytes to pixels + lowlow_pitch /= sizeof(PIXEL); + lowhigh_pitch /= sizeof(PIXEL); + highlow_pitch /= sizeof(PIXEL); + highhigh_pitch /= sizeof(PIXEL); + output_pitch /= sizeof(PIXEL); + + // Initialize the pointers to the even and odd output rows + even_output = output; + odd_output = output + output_pitch; + + // Apply the vertical border filter to the first row + row = 0; + + // Set pointers to the first three rows in the first highpass band + lowhigh_row[0] = lowhigh + 0 * lowhigh_pitch; + lowhigh_row[1] = lowhigh + 1 * lowhigh_pitch; + lowhigh_row[2] = lowhigh + 2 * lowhigh_pitch; + + // Dequantize three rows of highpass coefficients in the first highpass band + DequantizeBandRow16s(lowhigh_row[0], input_width, lowhigh_quantization, lowhigh_line[0]); + DequantizeBandRow16s(lowhigh_row[1], input_width, lowhigh_quantization, lowhigh_line[1]); + DequantizeBandRow16s(lowhigh_row[2], input_width, lowhigh_quantization, lowhigh_line[2]); + + // Dequantize one row of coefficients each in the second and third highpass bands + DequantizeBandRow16s(highlow, input_width, highlow_quantization, highlow_line); + DequantizeBandRow16s(highhigh, input_width, highhigh_quantization, highhigh_line); + + for (column = 0; column < input_width; column++) + { + int32_t even = 0; // Result of convolution with even filter + int32_t odd = 0; // Result of convolution with odd filter + + + /***** Compute the vertical inverse for the left two bands *****/ + + // Apply the even reconstruction filter to the lowpass band + even += 11 * lowlow[column + 0 * lowlow_pitch]; + even -= 4 * lowlow[column + 1 * lowlow_pitch]; + even += 1 * lowlow[column + 2 * lowlow_pitch]; + even += rounding; + even = DivideByShift(even, 3); + + // Add the highpass correction + even += highlow_line[column]; + even >>= 1; + + // The inverse of the left two bands should be a positive number + //assert(0 <= even && even <= INT16_MAX); + + // Place the even result in the even row + even_lowpass[column] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd += 5 * lowlow[column + 0 * lowlow_pitch]; + odd += 4 * lowlow[column + 1 * lowlow_pitch]; + odd -= 1 * lowlow[column + 2 * lowlow_pitch]; + odd += rounding; + odd = DivideByShift(odd, 3); + + // Subtract the highpass correction + odd -= highlow_line[column]; + odd >>= 1; + + // The inverse of the left two bands should be a positive number + //assert(0 <= odd && odd <= INT16_MAX); + + // Place the odd result in the odd row + odd_lowpass[column] = ClampPixel(odd); + + + /***** Compute the vertical inverse for the right two bands *****/ + + even = 0; + odd = 0; + + // Apply the even reconstruction filter to the lowpass band + even += 11 * lowhigh_line[0][column]; + even -= 4 * lowhigh_line[1][column]; + even += 1 * lowhigh_line[2][column]; + even += rounding; + even = DivideByShift(even, 3); + + // Add the highpass correction + even += highhigh_line[column]; + even >>= 1; + + // Place the even result in the even row + even_highpass[column] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd += 5 * lowhigh_line[0][column]; + odd += 4 * lowhigh_line[1][column]; + odd -= 1 * lowhigh_line[2][column]; + odd += rounding; + odd = DivideByShift(odd, 3); + + // Subtract the highpass correction + odd -= highhigh_line[column]; + odd >>= 1; + + // Place the odd result in the odd row + odd_highpass[column] = ClampPixel(odd); + } + + // Apply the inverse horizontal transform to the even and odd rows + InvertHorizontal16s(even_lowpass, even_highpass, even_output, input_width, output_width); + InvertHorizontal16s(odd_lowpass, odd_highpass, odd_output, input_width, output_width); + + // Advance to the next pair of even and odd output rows + even_output += 2 * output_pitch; + odd_output += 2 * output_pitch; + + // Always advance the highpass row pointers + highlow += highlow_pitch; + highhigh += highhigh_pitch; + + // Advance the row index + row++; + + // Process the middle rows using the interior reconstruction filters + for (; row < last_row; row++) + { + // Dequantize one row from each of the two highpass bands + DequantizeBandRow16s(highlow, input_width, highlow_quantization, highlow_line); + DequantizeBandRow16s(highhigh, input_width, highhigh_quantization, highhigh_line); + + // Process the entire row + for (column = 0; column < input_width; column++) + { + int32_t even = 0; // Result of convolution with even filter + int32_t odd = 0; // Result of convolution with odd filter + + + /***** Compute the vertical inverse for the left two bands *****/ + + // Apply the even reconstruction filter to the lowpass band + even += lowlow[column + 0 * lowlow_pitch]; + even -= lowlow[column + 2 * lowlow_pitch]; + even += 4; + even >>= 3; + even += lowlow[column + 1 * lowlow_pitch]; + + // Add the highpass correction + even += highlow_line[column]; + even >>= 1; + + // The inverse of the left two bands should be a positive number + //assert(0 <= even && even <= INT16_MAX); + + // Place the even result in the even row + even_lowpass[column] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd -= lowlow[column + 0 * lowlow_pitch]; + odd += lowlow[column + 2 * lowlow_pitch]; + odd += 4; + odd >>= 3; + odd += lowlow[column + 1 * lowlow_pitch]; + + // Subtract the highpass correction + odd -= highlow_line[column]; + odd >>= 1; + + // The inverse of the left two bands should be a positive number + //assert(0 <= odd && odd <= INT16_MAX); + + // Place the odd result in the odd row + odd_lowpass[column] = ClampPixel(odd); + + + /***** Compute the vertical inverse for the right two bands *****/ + + even = 0; + odd = 0; + + // Apply the even reconstruction filter to the lowpass band + even += lowhigh_line[0][column]; + even -= lowhigh_line[2][column]; + even += 4; + even >>= 3; + even += lowhigh_line[1][column]; + + // Add the highpass correction + even += highhigh_line[column]; + even >>= 1; + + // Place the even result in the even row + even_highpass[column] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd -= lowhigh_line[0][column]; + odd += lowhigh_line[2][column]; + odd += 4; + odd >>= 3; + odd += lowhigh_line[1][column]; + + // Subtract the highpass correction + odd -= highhigh_line[column]; + odd >>= 1; + + // Place the odd result in the odd row + odd_highpass[column] = ClampPixel(odd); + } + + // Apply the inverse horizontal transform to the even and odd rows and descale the results + InvertHorizontal16s(even_lowpass, even_highpass, even_output, input_width, output_width); + InvertHorizontal16s(odd_lowpass, odd_highpass, odd_output, input_width, output_width); + + // Advance to the next input row in each band + lowlow += lowlow_pitch; + lowhigh += lowhigh_pitch; + highlow += highlow_pitch; + highhigh += highhigh_pitch; + + // Advance to the next pair of even and odd output rows + even_output += 2 * output_pitch; + odd_output += 2 * output_pitch; + + if (row < last_row - 1) + { + // Compute the address of the next row in the lowhigh band + PIXEL *lowhigh_row_ptr = (lowhigh + 2 * lowhigh_pitch); + //PIXEL *lowhigh_row_ptr = (lowhigh + lowhigh_pitch); + + // Shift the rows in the buffer of dequantized lowhigh bands + PIXEL *temp = lowhigh_line[0]; + lowhigh_line[0] = lowhigh_line[1]; + lowhigh_line[1] = lowhigh_line[2]; + lowhigh_line[2] = temp; + + // Undo quantization for the next row in the lowhigh band + DequantizeBandRow16s(lowhigh_row_ptr, input_width, lowhigh_quantization, lowhigh_line[2]); + } + } + + // Should have exited the loop at the last row + assert(row == last_row); + + // Advance the lowlow pointer to the last row in the band + lowlow += lowlow_pitch; + + // Check that the band pointers are on the last row in each wavelet band + assert(lowlow == (lowlow_band + last_row * lowlow_pitch)); + + assert(highlow == (highlow_band + last_row * highlow_pitch)); + assert(highhigh == (highhigh_band + last_row * highhigh_pitch)); + + // Undo quantization for the highlow and highhigh bands + DequantizeBandRow16s(highlow, input_width, highlow_quantization, highlow_line); + DequantizeBandRow16s(highhigh, input_width, highhigh_quantization, highhigh_line); + + // Apply the vertical border filter to the last row + for (column = 0; column < input_width; column++) + { + int32_t even = 0; // Result of convolution with even filter + int32_t odd = 0; // Result of convolution with odd filter + + + /***** Compute the vertical inverse for the left two bands *****/ + + // Apply the even reconstruction filter to the lowpass band + even += 5 * lowlow[column + 0 * lowlow_pitch]; + even += 4 * lowlow[column - 1 * lowlow_pitch]; + even -= 1 * lowlow[column - 2 * lowlow_pitch]; + even += 4; + even = DivideByShift(even, 3); + + // Add the highpass correction + even += highlow_line[column]; + even >>= 1; + + // The inverse of the left two bands should be a positive number + //assert(0 <= even && even <= INT16_MAX); + + // Place the even result in the even row + even_lowpass[column] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd += 11 * lowlow[column + 0 * lowlow_pitch]; + odd -= 4 * lowlow[column - 1 * lowlow_pitch]; + odd += 1 * lowlow[column - 2 * lowlow_pitch]; + odd += 4; + odd = DivideByShift(odd, 3); + + // Subtract the highpass correction + odd -= highlow_line[column]; + odd >>= 1; + + // The inverse of the left two bands should be a positive number + //assert(0 <= odd && odd <= INT16_MAX); + + // Place the odd result in the odd row + odd_lowpass[column] = ClampPixel(odd); + + + // Compute the vertical inverse for the right two bands // + + even = 0; + odd = 0; + + // Apply the even reconstruction filter to the lowpass band + even += 5 * lowhigh_line[2][column]; + even += 4 * lowhigh_line[1][column]; + even -= 1 * lowhigh_line[0][column]; + even += 4; + even = DivideByShift(even, 3); + + // Add the highpass correction + even += highhigh_line[column]; + even >>= 1; + + // Place the even result in the even row + even_highpass[column] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd += 11 * lowhigh_line[2][column]; + odd -= 4 * lowhigh_line[1][column]; + odd += 1 * lowhigh_line[0][column]; + odd += 4; + odd = DivideByShift(odd, 3); + + // Subtract the highpass correction + odd -= highhigh_line[column]; + odd >>= 1; + + // Place the odd result in the odd row + odd_highpass[column] = ClampPixel(odd); + } + + // Apply the inverse horizontal transform to the even and odd rows and descale the results + InvertHorizontal16s(even_lowpass, even_highpass, even_output, input_width, output_width); + + // Is the output wavelet shorter than twice the height of the input wavelet? + if (2 * row + 1 < output_height) { + InvertHorizontal16s(odd_lowpass, odd_highpass, odd_output, input_width, output_width); + } + + // Free the scratch buffers + allocator->Free(even_lowpass); + allocator->Free(even_highpass); + allocator->Free(odd_lowpass); + allocator->Free(odd_highpass); + + allocator->Free(lowhigh_line[0]); + allocator->Free(lowhigh_line[1]); + allocator->Free(lowhigh_line[2]); + allocator->Free(highlow_line); + allocator->Free(highhigh_line); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Apply the inverse spatial transform with descaling + This routine is similar to @ref InvertSpatialQuant16s, but a scale factor + that was applied during encoding is removed from the output values. + */ +CODEC_ERROR InvertSpatialQuantDescale16s(gpr_allocator *allocator, + PIXEL *lowlow_band, int lowlow_pitch, + PIXEL *lowhigh_band, int lowhigh_pitch, + PIXEL *highlow_band, int highlow_pitch, + PIXEL *highhigh_band, int highhigh_pitch, + PIXEL *output_image, int output_pitch, + DIMENSION input_width, DIMENSION input_height, + DIMENSION output_width, DIMENSION output_height, + int descale, QUANT quantization[]) +{ + PIXEL *lowlow = lowlow_band; + PIXEL *lowhigh = lowhigh_band; + PIXEL *highlow = highlow_band; + PIXEL *highhigh = highhigh_band; + PIXEL *output = output_image; + PIXEL *even_lowpass; + PIXEL *even_highpass; + PIXEL *odd_lowpass; + PIXEL *odd_highpass; + PIXEL *even_output; + PIXEL *odd_output; + size_t buffer_row_size; + int last_row = input_height - 1; + int row, column; + + PIXEL *lowhigh_row[3]; + + PIXEL *lowhigh_line[3]; + PIXEL *highlow_line; + PIXEL *highhigh_line; + + QUANT highlow_quantization = quantization[HL_BAND]; + QUANT lowhigh_quantization = quantization[LH_BAND]; + QUANT highhigh_quantization = quantization[HH_BAND]; + + // Compute positions within the temporary buffer for each row of horizontal lowpass + // and highpass intermediate coefficients computed by the vertical inverse transform + buffer_row_size = input_width * sizeof(PIXEL); + + // Allocate space for the even and odd rows of results from the inverse vertical filter + even_lowpass = (PIXEL *)allocator->Alloc(buffer_row_size); + even_highpass = (PIXEL *)allocator->Alloc(buffer_row_size); + odd_lowpass = (PIXEL *)allocator->Alloc(buffer_row_size); + odd_highpass = (PIXEL *)allocator->Alloc(buffer_row_size); + + // Allocate scratch space for the dequantized highpass coefficients + lowhigh_line[0] = (PIXEL *)allocator->Alloc(buffer_row_size); + lowhigh_line[1] = (PIXEL *)allocator->Alloc(buffer_row_size); + lowhigh_line[2] = (PIXEL *)allocator->Alloc(buffer_row_size); + highlow_line = (PIXEL *)allocator->Alloc(buffer_row_size); + highhigh_line = (PIXEL *)allocator->Alloc(buffer_row_size); + + // Convert pitch from bytes to pixels + lowlow_pitch /= sizeof(PIXEL); + lowhigh_pitch /= sizeof(PIXEL); + highlow_pitch /= sizeof(PIXEL); + highhigh_pitch /= sizeof(PIXEL); + output_pitch /= sizeof(PIXEL); + + // Initialize the pointers to the even and odd output rows + even_output = output; + odd_output = output + output_pitch; + + // Apply the vertical border filter to the first row + row = 0; + + // Set pointers to the first three rows in the first highpass band + lowhigh_row[0] = lowhigh + 0 * lowhigh_pitch; + lowhigh_row[1] = lowhigh + 1 * lowhigh_pitch; + lowhigh_row[2] = lowhigh + 2 * lowhigh_pitch; + + // Dequantize three rows of highpass coefficients in the first highpass band + DequantizeBandRow16s(lowhigh_row[0], input_width, lowhigh_quantization, lowhigh_line[0]); + DequantizeBandRow16s(lowhigh_row[1], input_width, lowhigh_quantization, lowhigh_line[1]); + DequantizeBandRow16s(lowhigh_row[2], input_width, lowhigh_quantization, lowhigh_line[2]); + + // Dequantize one row of coefficients each in the second and third highpass bands + DequantizeBandRow16s(highlow, input_width, highlow_quantization, highlow_line); + DequantizeBandRow16s(highhigh, input_width, highhigh_quantization, highhigh_line); + + for (column = 0; column < input_width; column++) + { + int32_t even = 0; // Result of convolution with even filter + int32_t odd = 0; // Result of convolution with odd filter + + + /***** Compute the vertical inverse for the left two bands *****/ + + // Apply the even reconstruction filter to the lowpass band + even += 11 * lowlow[column + 0 * lowlow_pitch]; + even -= 4 * lowlow[column + 1 * lowlow_pitch]; + even += 1 * lowlow[column + 2 * lowlow_pitch]; + even += rounding; + even = DivideByShift(even, 3); + + // Add the highpass correction + even += highlow_line[column]; + even = DivideByShift(even, 1); + + // The inverse of the left two bands should be a positive number + //assert(0 <= even && even <= INT16_MAX); + + // Place the even result in the even row + even_lowpass[column] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd += 5 * lowlow[column + 0 * lowlow_pitch]; + odd += 4 * lowlow[column + 1 * lowlow_pitch]; + odd -= 1 * lowlow[column + 2 * lowlow_pitch]; + odd += rounding; + odd = DivideByShift(odd, 3); + + // Subtract the highpass correction + odd -= highlow_line[column]; + odd = DivideByShift(odd, 1); + + // The inverse of the left two bands should be a positive number + //assert(0 <= odd && odd <= INT16_MAX); + + // Place the odd result in the odd row + odd_lowpass[column] = ClampPixel(odd); + + + /***** Compute the vertical inverse for the right two bands *****/ + + even = 0; + odd = 0; + + // Apply the even reconstruction filter to the lowpass band + even += 11 * lowhigh_line[0][column]; + even -= 4 * lowhigh_line[1][column]; + even += 1 * lowhigh_line[2][column]; + even += rounding; + even = DivideByShift(even, 3); + + // Add the highpass correction + even += highhigh_line[column]; + even = DivideByShift(even, 1); + + // Place the even result in the even row + even_highpass[column] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd += 5 * lowhigh_line[0][column]; + odd += 4 * lowhigh_line[1][column]; + odd -= 1 * lowhigh_line[2][column]; + odd += rounding; + odd = DivideByShift(odd, 3); + + // Subtract the highpass correction + odd -= highhigh_line[column]; + odd = DivideByShift(odd, 1); + + // Place the odd result in the odd row + odd_highpass[column] = ClampPixel(odd); + } + + // Apply the inverse horizontal transform to the even and odd rows and descale the results + InvertHorizontalDescale16s(even_lowpass, even_highpass, even_output, + input_width, output_width, descale); + + InvertHorizontalDescale16s(odd_lowpass, odd_highpass, odd_output, + input_width, output_width, descale); + + // Advance to the next pair of even and odd output rows + even_output += 2 * output_pitch; + odd_output += 2 * output_pitch; + + // Always advance the highpass row pointers + highlow += highlow_pitch; + highhigh += highhigh_pitch; + + // Advance the row index + row++; + + // Process the middle rows using the interior reconstruction filters + for (; row < last_row; row++) + { + // Dequantize one row from each of the two highpass bands + DequantizeBandRow16s(highlow, input_width, highlow_quantization, highlow_line); + DequantizeBandRow16s(highhigh, input_width, highhigh_quantization, highhigh_line); + + // Process the entire row + for (column = 0; column < input_width; column++) + { + int32_t even = 0; // Result of convolution with even filter + int32_t odd = 0; // Result of convolution with odd filter + + + /***** Compute the vertical inverse for the left two bands *****/ + + // Apply the even reconstruction filter to the lowpass band + even += lowlow[column + 0 * lowlow_pitch]; + even -= lowlow[column + 2 * lowlow_pitch]; + even += 4; + even >>= 3; + even += lowlow[column + 1 * lowlow_pitch]; + + // Add the highpass correction + even += highlow_line[column]; + even = DivideByShift(even, 1); + + // The inverse of the left two bands should be a positive number + //assert(0 <= even && even <= INT16_MAX); + + // Place the even result in the even row + even_lowpass[column] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd -= lowlow[column + 0 * lowlow_pitch]; + odd += lowlow[column + 2 * lowlow_pitch]; + odd += 4; + odd >>= 3; + odd += lowlow[column + 1 * lowlow_pitch]; + + // Subtract the highpass correction + odd -= highlow_line[column]; + odd = DivideByShift(odd, 1); + + // The inverse of the left two bands should be a positive number + //assert(0 <= odd && odd <= INT16_MAX); + + // Place the odd result in the odd row + odd_lowpass[column] = ClampPixel(odd); + + + /***** Compute the vertical inverse for the right two bands *****/ + + even = 0; + odd = 0; + + // Apply the even reconstruction filter to the lowpass band + even += lowhigh_line[0][column]; + even -= lowhigh_line[2][column]; + even += 4; + even >>= 3; + even += lowhigh_line[1][column]; + + // Add the highpass correction + even += highhigh_line[column]; + even = DivideByShift(even, 1); + + // Place the even result in the even row + even_highpass[column] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd -= lowhigh_line[0][column]; + odd += lowhigh_line[2][column]; + odd += 4; + odd >>= 3; + odd += lowhigh_line[1][column]; + + // Subtract the highpass correction + odd -= highhigh_line[column]; + odd = DivideByShift(odd, 1); + + // Place the odd result in the odd row + odd_highpass[column] = ClampPixel(odd); + } + + // Apply the inverse horizontal transform to the even and odd rows and descale the results + InvertHorizontalDescale16s(even_lowpass, even_highpass, even_output, + input_width, output_width, descale); + + InvertHorizontalDescale16s(odd_lowpass, odd_highpass, odd_output, + input_width, output_width, descale); + + // Advance to the next input row in each band + lowlow += lowlow_pitch; + lowhigh += lowhigh_pitch; + highlow += highlow_pitch; + highhigh += highhigh_pitch; + + // Advance to the next pair of even and odd output rows + even_output += 2 * output_pitch; + odd_output += 2 * output_pitch; + + if (row < last_row - 1) + { + // Compute the address of the next row in the lowhigh band + PIXEL *lowhigh_row_ptr = (lowhigh + 2 * lowhigh_pitch); + + // Shift the rows in the buffer of dequantized lowhigh bands + PIXEL *temp = lowhigh_line[0]; + lowhigh_line[0] = lowhigh_line[1]; + lowhigh_line[1] = lowhigh_line[2]; + lowhigh_line[2] = temp; + + // Undo quantization for the next row in the lowhigh band + DequantizeBandRow16s(lowhigh_row_ptr, input_width, lowhigh_quantization, lowhigh_line[2]); + } + } + + // Should have exited the loop at the last row + assert(row == last_row); + + // Advance the lowlow pointer to the last row in the band + lowlow += lowlow_pitch; + + // Check that the band pointers are on the last row in each wavelet band + assert(lowlow == (lowlow_band + last_row * lowlow_pitch)); + + assert(highlow == (highlow_band + last_row * highlow_pitch)); + assert(highhigh == (highhigh_band + last_row * highhigh_pitch)); + + // Undo quantization for the highlow and highhigh bands + DequantizeBandRow16s(highlow, input_width, highlow_quantization, highlow_line); + DequantizeBandRow16s(highhigh, input_width, highhigh_quantization, highhigh_line); + + // Apply the vertical border filter to the last row + for (column = 0; column < input_width; column++) + { + int32_t even = 0; // Result of convolution with even filter + int32_t odd = 0; // Result of convolution with odd filter + + + /***** Compute the vertical inverse for the left two bands *****/ + + // Apply the even reconstruction filter to the lowpass band + even += 5 * lowlow[column + 0 * lowlow_pitch]; + even += 4 * lowlow[column - 1 * lowlow_pitch]; + even -= 1 * lowlow[column - 2 * lowlow_pitch]; + even += rounding; + even = DivideByShift(even, 3); + + // Add the highpass correction + even += highlow_line[column]; + even = DivideByShift(even, 1); + + // The inverse of the left two bands should be a positive number + //assert(0 <= even && even <= INT16_MAX); + + // Place the even result in the even row + even_lowpass[column] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd += 11 * lowlow[column + 0 * lowlow_pitch]; + odd -= 4 * lowlow[column - 1 * lowlow_pitch]; + odd += 1 * lowlow[column - 2 * lowlow_pitch]; + odd += rounding; + odd = DivideByShift(odd, 3); + + // Subtract the highpass correction + odd -= highlow_line[column]; + odd = DivideByShift(odd, 1); + + // The inverse of the left two bands should be a positive number + //assert(0 <= odd && odd <= INT16_MAX); + + // Place the odd result in the odd row + odd_lowpass[column] = ClampPixel(odd); + + + /***** Compute the vertical inverse for the right two bands *****/ + + even = 0; + odd = 0; + + // Apply the even reconstruction filter to the lowpass band + even += 5 * lowhigh_line[2][column]; + even += 4 * lowhigh_line[1][column]; + even -= 1 * lowhigh_line[0][column]; + even += rounding; + even = DivideByShift(even, 3); + + // Add the highpass correction + even += highhigh_line[column]; + even = DivideByShift(even, 1); + + // Place the even result in the even row + even_highpass[column] = ClampPixel(even); + + // Apply the odd reconstruction filter to the lowpass band + odd += 11 * lowhigh_line[2][column]; + odd -= 4 * lowhigh_line[1][column]; + odd += 1 * lowhigh_line[0][column]; + odd += rounding; + odd = DivideByShift(odd, 3); + + // Subtract the highpass correction + odd -= highhigh_line[column]; + odd = DivideByShift(odd, 1); + + // Place the odd result in the odd row + odd_highpass[column] = ClampPixel(odd); + } + + // Apply the inverse horizontal transform to the even and odd rows and descale the results + InvertHorizontalDescale16s(even_lowpass, even_highpass, even_output, + input_width, output_width, descale); + + // Is the output wavelet shorter than twice the height of the input wavelet? + if (2 * row + 1 < output_height) { + InvertHorizontalDescale16s(odd_lowpass, odd_highpass, odd_output, + input_width, output_width, descale); + } + + // Free the scratch buffers + allocator->Free(even_lowpass); + allocator->Free(even_highpass); + allocator->Free(odd_lowpass); + allocator->Free(odd_highpass); + + allocator->Free(lowhigh_line[0]); + allocator->Free(lowhigh_line[1]); + allocator->Free(lowhigh_line[2]); + allocator->Free(highlow_line); + allocator->Free(highhigh_line); + + return CODEC_ERROR_OKAY; +} + diff --git a/source/lib/vc5_decoder/inverse.h b/source/lib/vc5_decoder/inverse.h new file mode 100755 index 0000000..0d8d77c --- /dev/null +++ b/source/lib/vc5_decoder/inverse.h @@ -0,0 +1,51 @@ +/*! @file inverse.h + * + * @brief Declaration of the inverse wavelet transform functions. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef INVERSE_H +#define INVERSE_H + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR InvertSpatialQuant16s(gpr_allocator *allocator, + PIXEL *lowlow_band, int lowlow_pitch, + PIXEL *lowhigh_band, int lowhigh_pitch, + PIXEL *highlow_band, int highlow_pitch, + PIXEL *highhigh_band, int highhigh_pitch, + PIXEL *output_image, int output_pitch, + DIMENSION input_width, DIMENSION input_height, + DIMENSION output_width, DIMENSION output_height, + QUANT quantization[]); + + CODEC_ERROR InvertSpatialQuantDescale16s(gpr_allocator *allocator, + PIXEL *lowlow_band, int lowlow_pitch, + PIXEL *lowhigh_band, int lowhigh_pitch, + PIXEL *highlow_band, int highlow_pitch, + PIXEL *highhigh_band, int highhigh_pitch, + PIXEL *output_image, int output_pitch, + DIMENSION input_width, DIMENSION input_height, + DIMENSION output_width, DIMENSION output_height, + //ROI roi, PIXEL *buffer, size_t buffer_size, + int descale, QUANT quantization[]); + +#ifdef __cplusplus +} +#endif + +#endif // INVERSE_H diff --git a/source/lib/vc5_decoder/parameters.c b/source/lib/vc5_decoder/parameters.c new file mode 100755 index 0000000..f8ef2cd --- /dev/null +++ b/source/lib/vc5_decoder/parameters.c @@ -0,0 +1,47 @@ +/*! @file parameters.c + * + * @brief Implementation of the data structure used to pass decoding + * parameters to the decoder. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +//! Current version number of the parameters data structure +#define PARAMETERS_VERSION 0 + +/*! + @brief Initialize the parameters data structure + + The version number of the parameters data structure must be + incremented whenever a change is made to the definition of + the parameters data structure. +*/ +CODEC_ERROR InitDecoderParameters(DECODER_PARAMETERS *parameters) +{ + memset(parameters, 0, sizeof(DECODER_PARAMETERS)); + parameters->version = 1; + parameters->verbose_flag = false; + + parameters->enabled_parts = VC5_ENABLED_PARTS; + + parameters->output.format = PIXEL_FORMAT_RAW_DEFAULT; + + parameters->rgb_resolution = GPR_RGB_RESOLUTION_NONE; + + gpr_rgb_gain_set_defaults(¶meters->rgb_gain); + + return CODEC_ERROR_OKAY; +} diff --git a/source/lib/vc5_decoder/parameters.h b/source/lib/vc5_decoder/parameters.h new file mode 100755 index 0000000..d0bbb74 --- /dev/null +++ b/source/lib/vc5_decoder/parameters.h @@ -0,0 +1,119 @@ +/*! @file parameters.h + * + * @brief Declare a data structure for holding a table of parameters used + * during decoding to override the default decoding behavior. + + * The decoder can be initialized using the dimensions of the encoded frame + * obtained from an external source such as a media container and the pixel + * format of the decoded frame. The encoded sample will be decoded to the + * dimensions of the encoded frame without at the full encoded resolution + * without scaling. The decoded frames will have the specified pixel format, + * but this assumes that the encoded dimensions used during initialization + * are the same as the actual encoded dimensions and that the pixel format of + * the decoded frames is a valid pixel format. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PARAMETERS_H +#define PARAMETERS_H + +#include "vc5_decoder.h" + +/*! + @brief Declaration of a data structure for passing decoding parameters to the decoder +*/ +typedef struct _decoder_parameters +{ + uint32_t version; //!< Version number for this definition of the parameters + + uint32_t enabled_parts; //!< Parts of the VC-5 standard that are enabled + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + int layer_count; //!< Number of layers in the sample + bool progressive; //!< True if the frame is progressive + bool top_field_first; //!< True if interlaced with top field first +#endif + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + bool section_flag; //!< True if decoding sections element in the bitstream +#endif + + //! Dimensions and format of the output of the image unpacking process + struct _input_parameters + { + DIMENSION width; + DIMENSION height; + // PIXEL_FORMAT format; + } input; //! Dimensions and format of the unpacked image + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + //! Dimensions and format of the output of the decoding process + struct _decoded_parameters + { + DIMENSION width; + DIMENSION height; + PIXEL_FORMAT format; + } decoded; //! Decoded image dimensions and pixel format +#endif + +#if VC5_ENABLED_PART(VC5_PART_ELEMENTARY) + //! Dimensions and format of the output of the image repacking process + struct _output_parameters + { + DIMENSION width; + DIMENSION height; + PIXEL_FORMAT format; + } output; +#endif + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + //! Dimensions and format of the displayable image + struct _display_parameters + { + DIMENSION width; + DIMENSION height; + PIXEL_FORMAT format; + } display; +#endif + +#if VC5_ENABLED_PART(VC5_PART_METADATA) + //! Metadata that controls decoding + METADATA metadata; +#endif + + //! Flag that controls verbose output + bool verbose_flag; + + GPR_RGB_RESOLUTION rgb_resolution; + + int rgb_bits; + + gpr_rgb_gain rgb_gain; + + gpr_allocator allocator; + +} DECODER_PARAMETERS; + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR InitDecoderParameters(DECODER_PARAMETERS *parameters); + +#ifdef __cplusplus +} +#endif + +#endif // PARAMETERS_H diff --git a/source/lib/vc5_decoder/raw.c b/source/lib/vc5_decoder/raw.c new file mode 100755 index 0000000..7cbac6f --- /dev/null +++ b/source/lib/vc5_decoder/raw.c @@ -0,0 +1,135 @@ +/*! @file raw.c + * + * @brief Definition of routines for packing a row of pixels into a RAW image. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +/*! + @brief Pack the component arrays into an output image + + The inverse component transform for Bayer images (VC-5 Part 3) + is applied to the component arrays before combining the values + into a packed image. + */ +CODEC_ERROR PackComponentsToRAW(const UNPACKED_IMAGE *image, + PIXEL *output_buffer, size_t output_pitch, + DIMENSION width, DIMENSION height, + ENABLED_PARTS enabled_parts, uint16_t output_bit_depth, PIXEL_FORMAT output_format ) +{ + // Define pointers to the rows for each input component + COMPONENT_VALUE *GS_input_buffer; + COMPONENT_VALUE *RG_input_buffer; + COMPONENT_VALUE *BG_input_buffer; + COMPONENT_VALUE *GD_input_buffer; + + // Define pointers to the rows for each output component + uint16_t *output_row1_ptr; + uint16_t *output_row2_ptr; + + //size_t input_quarter_pitch; + size_t output_half_pitch; + + int row; + + GS_input_buffer = image->component_array_list[0].data; + RG_input_buffer = image->component_array_list[1].data; + BG_input_buffer = image->component_array_list[2].data; + GD_input_buffer = image->component_array_list[3].data; + + // Compute the distance between the half rows in the Bayer grid + output_half_pitch = output_pitch / 2; + + for (row = 0; row < height; row++) + { + COMPONENT_VALUE *GS_input_row_ptr = (COMPONENT_VALUE *)((uintptr_t)GS_input_buffer + row * image->component_array_list[0].pitch); + COMPONENT_VALUE *RG_input_row_ptr = (COMPONENT_VALUE *)((uintptr_t)RG_input_buffer + row * image->component_array_list[1].pitch); + COMPONENT_VALUE *BG_input_row_ptr = (COMPONENT_VALUE *)((uintptr_t)BG_input_buffer + row * image->component_array_list[2].pitch); + COMPONENT_VALUE *GD_input_row_ptr = (COMPONENT_VALUE *)((uintptr_t)GD_input_buffer + row * image->component_array_list[3].pitch); + + uint8_t *output_row_ptr = (uint8_t *)output_buffer + row * output_pitch; + + const int32_t midpoint = 2048; + + int column; + + output_row1_ptr = (uint16_t *)output_row_ptr; + output_row2_ptr = (uint16_t *)(output_row_ptr + output_half_pitch); + + // Pack the rows of Bayer components into the BYR4 pattern + for (column = 0; column < width; column++) + { + int32_t GS, RG, BG, GD; + int32_t R, G1, G2, B; + + GS = GS_input_row_ptr[column]; + RG = RG_input_row_ptr[column]; + BG = BG_input_row_ptr[column]; + GD = GD_input_row_ptr[column]; + + // Convert unsigned values to signed values + GD -= midpoint; + RG -= midpoint; + BG -= midpoint; + + R = (RG << 1) + GS; + B = (BG << 1) + GS; + G1 = GS + GD; + G2 = GS - GD; + + R = clamp_uint(R, 12); + G1 = clamp_uint(G1, 12); + G2 = clamp_uint(G2, 12); + B = clamp_uint(B, 12); + + // Apply inverse protune log curve + R = DecoderLogCurve[R]; + B = DecoderLogCurve[B]; + G1 = DecoderLogCurve[G1]; + G2 = DecoderLogCurve[G2]; + + R >>= (16 - output_bit_depth); + B >>= (16 - output_bit_depth); + G1 >>= (16 - output_bit_depth); + G2 >>= (16 - output_bit_depth); + + switch (output_format) + { + case PIXEL_FORMAT_RAW_RGGB_12: + case PIXEL_FORMAT_RAW_RGGB_14: + output_row1_ptr[2 * column + 0] = (uint16_t)R; + output_row1_ptr[2 * column + 1] = (uint16_t)G1; + output_row2_ptr[2 * column + 0] = (uint16_t)G2; + output_row2_ptr[2 * column + 1] = (uint16_t)B; + break; + + case PIXEL_FORMAT_RAW_GBRG_12: + case PIXEL_FORMAT_RAW_GBRG_14: + output_row1_ptr[2 * column + 0] = (uint16_t)G1; + output_row1_ptr[2 * column + 1] = (uint16_t)B; + output_row2_ptr[2 * column + 0] = (uint16_t)R; + output_row2_ptr[2 * column + 1] = (uint16_t)G2; + break; + + default: + assert(0); + break; + } + } + } + + return CODEC_ERROR_OKAY; +} diff --git a/source/lib/vc5_decoder/raw.h b/source/lib/vc5_decoder/raw.h new file mode 100755 index 0000000..7bca498 --- /dev/null +++ b/source/lib/vc5_decoder/raw.h @@ -0,0 +1,35 @@ +/*! @file raw.h + * + * @brief Declaration of routines for packing a row of pixels into a RAW image. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RAW_H +#define RAW_H + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR PackComponentsToRAW(const UNPACKED_IMAGE *image, + PIXEL *output_buffer, size_t output_pitch, + DIMENSION width, DIMENSION height, + ENABLED_PARTS enabled_parts, uint16_t output_bit_depth, PIXEL_FORMAT output_format ); + +#ifdef __cplusplus +} +#endif + +#endif // RAW_H diff --git a/source/lib/vc5_decoder/syntax.c b/source/lib/vc5_decoder/syntax.c new file mode 100755 index 0000000..f0c4b58 --- /dev/null +++ b/source/lib/vc5_decoder/syntax.c @@ -0,0 +1,116 @@ +/*! @file syntax.c + * + * @brief Implementation of functions for parsing the bitstream syntax of encoded samples. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +//! Size of a tag or value (in bits) +#define BITSTREAM_TAG_SIZE 16 + +/*! + @brief Read the next tag-valie pair from the bitstream. + + The next tag is read from the bitstream and the next value that + immediately follows the tag in the bitstreeam are read from the + bitstream. + + The value may be the length of the payload in bytes or the value + may be a single scalar. This routine only reads the next tag and + value and does not intepret the tag or value and does not read any + data that may follow the segment in the bitstream. + + The tag and value are interpreted by @ref UpdateCodecState and that + routine may read additional information from the bitstream. + + If the value is the length of the payload then it encodes the number + of bytes in the segment payload, not counting the segment header. +*/ +TAGVALUE GetSegment(BITSTREAM *stream) +{ + TAGVALUE segment; + segment.tuple.tag = (TAGWORD)GetBits(stream, 16); + segment.tuple.value = (TAGWORD)GetBits(stream, 16); + return segment; +} + +/*! + @brief Read the specified tag from the bitstream and return the value +*/ +TAGWORD GetValue(BITSTREAM *stream, int tag) +{ + TAGVALUE segment = GetTagValue(stream); + + assert(stream->error == BITSTREAM_ERROR_OKAY); + if (stream->error == BITSTREAM_ERROR_OKAY) { + assert(segment.tuple.tag == tag); + if (segment.tuple.tag == tag) { + return segment.tuple.value; + } + else { + stream->error = BITSTREAM_ERROR_BADTAG; + } + } + + // An error has occurred so return zero (error code was set above) + return 0; +} + +/*! + @brief Read the next tag value pair from the bitstream +*/ +TAGVALUE GetTagValue(BITSTREAM *stream) +{ + TAGVALUE segment = GetSegment(stream); + while (segment.tuple.tag < 0) { + segment = GetSegment(stream); + } + + return segment; +} + +/*! + @brief Return true if the tag is optional +*/ +bool IsTagOptional(TAGWORD tag) +{ + return (tag < 0); +} + +/*! + @brief Return true if the tag is required +*/ +bool IsTagRequired(TAGWORD tag) +{ + return (tag >= 0); +} + +/*! + @brief Return true if a valid tag read from the bitstream +*/ +bool IsValidSegment(BITSTREAM *stream, TAGVALUE segment, TAGWORD tag) +{ + return (stream->error == BITSTREAM_ERROR_OKAY && + segment.tuple.tag == tag); +} + +/*! + @brief Return true if the tag value pair has the specified tag and value +*/ +bool IsTagValue(TAGVALUE segment, int tag, TAGWORD value) +{ + return (segment.tuple.tag == tag && segment.tuple.value == value); +} diff --git a/source/lib/vc5_decoder/syntax.h b/source/lib/vc5_decoder/syntax.h new file mode 100755 index 0000000..d5c4099 --- /dev/null +++ b/source/lib/vc5_decoder/syntax.h @@ -0,0 +1,44 @@ +/*! @file syntax.h + * + * @brief Declare functions for parsing the bitstream syntax of encoded samples. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef DECODER_SYNTAX_H +#define DECODER_SYNTAX_H + +#ifdef __cplusplus +extern "C" { +#endif + + TAGVALUE GetSegment(BITSTREAM *stream); + + TAGWORD GetValue(BITSTREAM *stream, int tag); + + TAGVALUE GetTagValue(BITSTREAM *stream); + + bool IsTagOptional(TAGWORD tag); + + bool IsTagRequired(TAGWORD tag); + + bool IsValidSegment(BITSTREAM *stream, TAGVALUE segment, TAGWORD tag); + + bool IsTagValue(TAGVALUE segment, int tag, TAGWORD value); + +#ifdef __cplusplus +} +#endif + +#endif // DECODER_SYNTAX_H diff --git a/source/lib/vc5_decoder/vc5_decoder.c b/source/lib/vc5_decoder/vc5_decoder.c new file mode 100755 index 0000000..f36ad4c --- /dev/null +++ b/source/lib/vc5_decoder/vc5_decoder.c @@ -0,0 +1,132 @@ +/*! @file vc5_decoder.c + * + * @brief Implementation of the top level vc5 decoder API. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +void vc5_decoder_parameters_set_default(vc5_decoder_parameters* decoding_parameters) +{ + decoding_parameters->enabled_parts = VC5_ENABLED_PARTS; + + decoding_parameters->pixel_format = VC5_DECODER_PIXEL_FORMAT_DEFAULT; + + decoding_parameters->rgb_resolution = VC5_DECODER_RGB_RESOLUTION_DEFAULT; + decoding_parameters->rgb_bits = 8; + + gpr_rgb_gain_set_defaults(&decoding_parameters->rgb_gain); +} + +CODEC_ERROR vc5_decoder_process(const vc5_decoder_parameters* decoding_parameters, /* vc5 decoding parameters */ + const gpr_buffer* vc5_buffer, /* vc5 input buffer. */ + gpr_buffer* raw_buffer, /* raw output buffer. */ + gpr_rgb_buffer* rgb_buffer) /* rgb output buffer */ +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + IMAGE output_image; + // Clear the members of the image data structure + InitImage(&output_image); + + STREAM input; + DECODER_PARAMETERS parameters; + + // Initialize the parameters passed to the decoder + InitDecoderParameters(¶meters); + + parameters.enabled_parts = decoding_parameters->enabled_parts; + + parameters.rgb_resolution = decoding_parameters->rgb_resolution; + parameters.rgb_bits = decoding_parameters->rgb_bits; + parameters.rgb_gain = decoding_parameters->rgb_gain; + + if( rgb_buffer == NULL ) + { + parameters.rgb_resolution = GPR_RGB_RESOLUTION_NONE; + } + + parameters.allocator.Alloc = decoding_parameters->mem_alloc; + parameters.allocator.Free = decoding_parameters->mem_free; + + switch( decoding_parameters->pixel_format ) + { + case VC5_DECODER_PIXEL_FORMAT_RGGB_12: + parameters.output.format = PIXEL_FORMAT_RAW_RGGB_12; + break; + + case VC5_DECODER_PIXEL_FORMAT_RGGB_14: + parameters.output.format = PIXEL_FORMAT_RAW_RGGB_14; + break; + + case VC5_DECODER_PIXEL_FORMAT_GBRG_12: + parameters.output.format = PIXEL_FORMAT_RAW_GBRG_12; + break; + + case VC5_DECODER_PIXEL_FORMAT_GBRG_14: + parameters.output.format = PIXEL_FORMAT_RAW_GBRG_14; + break; + + default: + assert(0); + } + + // Check that the enabled parts are correct + error = CheckEnabledParts(¶meters.enabled_parts); + if (error != CODEC_ERROR_OKAY) { + return CODEC_ERROR_ENABLED_PARTS; + } + + error = OpenStreamBuffer(&input, vc5_buffer->buffer, vc5_buffer->size ); + if (error != CODEC_ERROR_OKAY) { + fprintf(stderr, "Could not open input vc5 stream\n" ); + return error; + } + + RGB_IMAGE rgb_image; + InitRGBImage(&rgb_image); + + error = DecodeImage(&input, &output_image, &rgb_image, ¶meters); + if (error != CODEC_ERROR_OKAY) { + fprintf(stderr, "Could not decode input vc5 bitstream. Error number %d\n", error ); + return error; + } + + if( parameters.rgb_resolution != GPR_RGB_RESOLUTION_NONE ) + { + assert( rgb_buffer); + + rgb_buffer->buffer = rgb_image.buffer; + rgb_buffer->size = rgb_image.size; + rgb_buffer->width = rgb_image.width; + rgb_buffer->height = rgb_image.height; + } + + if( raw_buffer ) + { + assert( output_image.buffer ); + assert( output_image.size == output_image.width * output_image.height * sizeof(unsigned short) ); + + raw_buffer->buffer = output_image.buffer; + raw_buffer->size = output_image.size; + } + else + { + // Nothing should be returned in output_image since we do not want output raw image + assert( output_image.buffer == NULL ); + } + + return error; +} diff --git a/source/lib/vc5_decoder/vc5_decoder.h b/source/lib/vc5_decoder/vc5_decoder.h new file mode 100755 index 0000000..c87111a --- /dev/null +++ b/source/lib/vc5_decoder/vc5_decoder.h @@ -0,0 +1,84 @@ +/*! @file vc5_decoder.h + * + * @brief Declaration of the top level vc5 decoder API. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VC5_DECODER_H +#define VC5_DECODER_H + +#include "error.h" +#include "types.h" +#include "gpr_buffer.h" +#include "gpr_rgb_buffer.h" +#include "vc5_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + + /*! + @brief Bayer pattern ordering for vc5 encoder processing + */ + typedef enum + { + VC5_DECODER_PIXEL_FORMAT_RGGB_12 = 0, // RGGB 14bit pixels packed into 16bits + + VC5_DECODER_PIXEL_FORMAT_RGGB_14 = 1, // RGGB 14bit pixels packed into 16bits + + VC5_DECODER_PIXEL_FORMAT_GBRG_12 = 2, // GBRG 12bit pixels packed into 16bits + + VC5_DECODER_PIXEL_FORMAT_GBRG_14 = 3, // GBRG 12bit pixels packed into 16bits + + VC5_DECODER_PIXEL_FORMAT_DEFAULT = VC5_DECODER_PIXEL_FORMAT_RGGB_14, + + } VC5_DECODER_PIXEL_FORMAT; + + #define VC5_DECODER_RGB_RESOLUTION_DEFAULT GPR_RGB_RESOLUTION_QUARTER + + /*! + @brief vc5 decoder parameters + */ + typedef struct + { + ENABLED_PARTS enabled_parts; + + VC5_DECODER_PIXEL_FORMAT pixel_format; // Bayer Ordering Pattern (Default: VC5_ENCODER_BAYER_ORDERING_RGGB) + + GPR_RGB_RESOLUTION rgb_resolution; + + int rgb_bits; + + gpr_rgb_gain rgb_gain; + + gpr_malloc mem_alloc; // Callback function to allocate memory + + gpr_free mem_free; // Callback function to free memory + + } vc5_decoder_parameters; + + void vc5_decoder_parameters_set_default(vc5_decoder_parameters* decoding_parameters); + + CODEC_ERROR vc5_decoder_process(const vc5_decoder_parameters* decoding_parameters, /* vc5 decoding parameters */ + const gpr_buffer* vc5_buffer, /* vc5 input buffer. */ + gpr_buffer* raw_buffer, /* raw output buffer. */ + gpr_rgb_buffer* rgb_buffer); /* rgb output buffer */ + + +#ifdef __cplusplus + } +#endif + +#endif // VC5_DECODER_H diff --git a/source/lib/vc5_decoder/vlc.c b/source/lib/vc5_decoder/vlc.c new file mode 100755 index 0000000..646fc59 --- /dev/null +++ b/source/lib/vc5_decoder/vlc.c @@ -0,0 +1,109 @@ +/*! @file vlc.c + * + * @brief Implementation of routines to parse a variable-length encoded bitstream. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +/*! + @brief Parse a run length coded magnitude in the bitstream +*/ +CODEC_ERROR GetRlv(BITSTREAM *stream, CODEBOOK *codebook, RUN *run) +{ + BITWORD bitstream_bits = 0; // Buffer of bits read from the stream + BITCOUNT bitstream_count = 0; // Number of bits read from the stream + + // Get the length of the codebook and initialize a pointer to its entries + int codebook_length = codebook->length; + RLV *codebook_entry = (RLV *)((uint8_t *)codebook + sizeof(CODEBOOK)); + + // Index into the codebook + int codeword_index = 0; + + // Search the codebook for the run length and value + while (codeword_index < codebook_length) + { + // Get the size of the current word in the codebook + BITCOUNT codeword_count = codebook_entry[codeword_index].size; + + // Need to read more bits from the stream? + if (bitstream_count < codeword_count) + { + // Calculate the number of additional bits to read from the stream + BITCOUNT read_count = codeword_count - bitstream_count; + bitstream_bits = AddBits(stream, bitstream_bits, read_count); + bitstream_count = codeword_count; + } + + // Examine the run length table entries that have the same bit field length + for (; (codeword_index < codebook_length) && (bitstream_count == codebook_entry[codeword_index].size); + codeword_index++) { + if (bitstream_bits == codebook_entry[codeword_index].bits) { + run->count = codebook_entry[codeword_index].count; + run->value = codebook_entry[codeword_index].value; + goto found; + } + } + } + + // Did not find a matching code in the codebook + return CODEC_ERROR_NOTFOUND; + +found: + + // Found a valid codeword in the bitstream + return CODEC_ERROR_OKAY; +} + +/*! + Parse a run length coded signed value in the bitstream +*/ +CODEC_ERROR GetRun(BITSTREAM *stream, CODEBOOK *codebook, RUN *run) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + int32_t value; + + // Get the magnitude of the number from the bitstream + error = GetRlv(stream, codebook, run); + + // Error while parsing the bitstream? + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Restore the sign to the magnitude of the run value + value = run->value; + + // Signed quantity? + if (value != 0) + { + BITWORD sign; + + // Something is wrong if the value is already negative + assert(value > 0); + + // Get the codeword for the sign of the value + sign = GetBits(stream, VLC_SIGNCODE_SIZE); + + // Change the sign if the codeword signalled a negative value + value = ((sign == VLC_NEGATIVE_CODE) ? neg(value) : value); + } + + // Return the signed value of the coefficient + run->value = value; + + return CODEC_ERROR_OKAY; +} diff --git a/source/lib/vc5_decoder/vlc.h b/source/lib/vc5_decoder/vlc.h new file mode 100755 index 0000000..6ec41f6 --- /dev/null +++ b/source/lib/vc5_decoder/vlc.h @@ -0,0 +1,103 @@ +/*! @file vlc.h + * + * @brief Declaration of the data structures for variable-length decoding + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VLC_H +#define VLC_H + +// Codewords for the sign bits that follow a non-zero value +#define VLC_POSITIVE_CODE 0x0 //!< Code that indicates a positive value +#define VLC_NEGATIVE_CODE 0x1 //!< Code that indicates a negative value +#define VLC_SIGNCODE_SIZE 1 //!< Size of the code for sign suffix + +/*! + @brief Codebook entries for arbitrary runs and values + + The codebook data structure allows runs of an arbitrary value, + but all codec implementations only use runs of zeros. The + codeword for a non-zero value is followed by the sign bit. + + @todo Could add the sign bit to the magnitude entries in this + table if it improves performance or makes the code more clear. +*/ +typedef struct _rlv { + uint_fast8_t size; //!< Size of code word in bits + uint32_t bits; //!< Code word bits right justified + uint32_t count; //!< Run length + int32_t value; //!< Run value (unsigned) +} RLV; + +/*! + @brief Declaration of a codebook + + This data structure is often called the master codebook to distinguish + it from the encoding tables that are derived from the codebook. The + codebook has a header that is immediately followed by the codebook entries. + Each entry is an @ref RLV data structure that contains the codeword and + the size of the codeword in bits. Each codeword represent a run length + and value. The current codec implementation only supports runs of zeros, + so the run length is one for non-zero values. A non-zero value is an + unsigned coefficient magnitude. Special codewords that mark significant + locations in the bitstream are indicated by a run length of zero and the + value indicates the type of marker. + + The codebook is generated by a separate program that takes as input a table + of the frequencies of coefficient magnitudes and runs of zeros. +*/ +typedef struct _codebook +{ + uint32_t length; //!< Number of entries in the code book + // The length is followed by the RLV entries +} CODEBOOK; + +//! Macro used to define the codebook generated by the Huffman program +#define RLVTABLE(n) \ + static struct \ + { \ + uint32_t length; \ + RLV entries[n]; \ + } + +/*! + @brief Structure returned by the run length decoding routines + + The value returned may be signed if the routine that was called + to parse the bitstream found a run of a non-zero value and then + parsed the sign bit that follows the magnitude. +*/ +typedef struct _run { + uint32_t count; //!< Run length count + int32_t value; //!< Run length value +} RUN; + +//! Initializer for the run length data structure +#define RUN_INITIALIZER {0, 0} + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR GetRlv(BITSTREAM *stream, CODEBOOK *codebook, RUN *run); + CODEC_ERROR GetRun(BITSTREAM *stream, CODEBOOK *codebook, RUN *run); + +#ifdef __cplusplus +} +#endif + +#endif // VLC_H diff --git a/source/lib/vc5_decoder/wavelet.c b/source/lib/vc5_decoder/wavelet.c new file mode 100755 index 0000000..a60eb0a --- /dev/null +++ b/source/lib/vc5_decoder/wavelet.c @@ -0,0 +1,173 @@ +/*! @file wavelet.c + * + * @brief Implementation of the module for wavelet data structures and transforms + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +/*! + @brief Apply the inverse wavelet transform to reconstruct a lowpass band + + This routine reconstructs the lowpass band in the output wavelet from the + decoded bands in the input wavelet. The prescale argument is used to undo + scaling that may have been performed during encoding to prevent overflow. + + @todo Replace the two different routines for different prescale shifts with + a single routine that can handle any prescale shift. +*/ +CODEC_ERROR TransformInverseSpatialQuantLowpass(gpr_allocator *allocator, WAVELET *input, WAVELET *output, uint16_t prescale) +{ + // Dimensions of each wavelet band + DIMENSION input_width = input->width; + DIMENSION input_height = input->height; + + // The output width may be less than twice the input width if the output width is odd + DIMENSION output_width = output->width; + + // The output height may be less than twice the input height if the output height is odd + DIMENSION output_height = output->height; + + // Check that a valid input image has been provided + assert(input != NULL); + assert(input->data[0] != NULL); + assert(input->data[1] != NULL); + assert(input->data[2] != NULL); + assert(input->data[3] != NULL); + + // Check that the output image is a gray image or a lowpass wavelet band + assert(output->data[0] != NULL); + + // Check for valid quantization values + if (input->quant[0] == 0) { + input->quant[0] = 1; + } + + assert(input->quant[0] > 0); + assert(input->quant[1] > 0); + assert(input->quant[2] > 0); + assert(input->quant[3] > 0); + + if (prescale > 1) + { + // This is a spatial transform for the lowpass temporal band + assert(prescale == 2); + + // Apply the inverse spatial transform for a lowpass band that is not prescaled + InvertSpatialQuantDescale16s(allocator, + (PIXEL *)input->data[0], input->pitch, + (PIXEL *)input->data[1], input->pitch, + (PIXEL *)input->data[2], input->pitch, + (PIXEL *)input->data[3], input->pitch, + output->data[0], output->pitch, + input_width, input_height, + output_width, output_height, + prescale, input->quant); + } + else + { + // This case does not handle any prescaling applied during encoding + assert(prescale == 0); + + // Apply the inverse spatial transform for a lowpass band that is not prescaled + InvertSpatialQuant16s(allocator, + (PIXEL *)input->data[0], input->pitch, + (PIXEL *)input->data[1], input->pitch, + (PIXEL *)input->data[2], input->pitch, + (PIXEL *)input->data[3], input->pitch, + output->data[0], output->pitch, + input_width, input_height, + output_width, output_height, + input->quant); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Apply the inverse wavelet transform to reconstruct a component array + + This routine reconstructs the lowpass band in the output wavelet from the + decoded bands in the input wavelet. The prescale argument is used to undo + scaling that may have been performed during encoding to prevent overflow. +*/ +CODEC_ERROR TransformInverseSpatialQuantArray(gpr_allocator *allocator, + WAVELET *input, + COMPONENT_VALUE *output_buffer, + DIMENSION output_width, + DIMENSION output_height, + size_t output_pitch, + PRESCALE prescale) +{ + // Dimensions of each wavelet band + DIMENSION input_width = input->width; + DIMENSION input_height = input->height; + + // Check that a valid input image has been provided + assert(input != NULL); + assert(input->data[0] != NULL); + assert(input->data[1] != NULL); + assert(input->data[2] != NULL); + assert(input->data[3] != NULL); + + // Check for valid quantization values + if (input->quant[0] == 0) { + input->quant[0] = 1; + } + + assert(input->quant[0] > 0); + assert(input->quant[1] > 0); + assert(input->quant[2] > 0); + assert(input->quant[3] > 0); + + assert(output_width > 0 && output_height > 0 && output_pitch > 0 && output_buffer != NULL); + + if (prescale > 1) + { + // This is a spatial transform for the lowpass temporal band + assert(prescale == 2); + + // Apply the inverse spatial transform for a lowpass band that is not prescaled + InvertSpatialQuantDescale16s(allocator, + (PIXEL *)input->data[0], input->pitch, + (PIXEL *)input->data[1], input->pitch, + (PIXEL *)input->data[2], input->pitch, + (PIXEL *)input->data[3], input->pitch, + (PIXEL *)output_buffer, (int)output_pitch, + input_width, input_height, + output_width, output_height, + prescale, input->quant); + } + else + { + // This case does not handle any prescaling applied during encoding + assert(prescale == 0); + + // Apply the inverse spatial transform for a lowpass band that is not prescaled + InvertSpatialQuant16s(allocator, + (PIXEL *)input->data[0], input->pitch, + (PIXEL *)input->data[1], input->pitch, + (PIXEL *)input->data[2], input->pitch, + (PIXEL *)input->data[3], input->pitch, + (PIXEL *)output_buffer, (int)output_pitch, + input_width, input_height, + output_width, output_height, + input->quant); + } + + return CODEC_ERROR_OKAY; +} diff --git a/source/lib/vc5_decoder/wavelet.h b/source/lib/vc5_decoder/wavelet.h new file mode 100755 index 0000000..4f2d2fb --- /dev/null +++ b/source/lib/vc5_decoder/wavelet.h @@ -0,0 +1,42 @@ +/*! @file wavelet.h + * + * @brief Declare of wavelet decoding functions + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _DECODER_WAVELET_H +#define _DECODER_WAVELET_H + +#ifdef __cplusplus +extern "C" { +#endif + +CODEC_ERROR TransformInverseSpatialQuantLowpass(gpr_allocator *allocator, WAVELET *input, WAVELET *output, uint16_t prescale); + +CODEC_ERROR TransformInverseSpatialQuantArray( gpr_allocator *allocator, + WAVELET *input, + COMPONENT_VALUE *output_buffer, + DIMENSION output_width, + DIMENSION output_height, + size_t output_pitch, + PRESCALE prescale); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/source/lib/vc5_encoder/CMakeLists.txt b/source/lib/vc5_encoder/CMakeLists.txt new file mode 100644 index 0000000..9dc143b --- /dev/null +++ b/source/lib/vc5_encoder/CMakeLists.txt @@ -0,0 +1,21 @@ +# library +set( LIB_NAME vc5_encoder ) + +# get source files +file( GLOB SRC_FILES "*.c" ) + +# get include files +file( GLOB INC_FILES "*.h" ) + +# add include files from other folders +include_directories( "../vc5_common" ) +include_directories( "../common/private" ) +include_directories( "../common/public" ) + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/source/lib/vc5_encoder/codebooks.c b/source/lib/vc5_encoder/codebooks.c new file mode 100755 index 0000000..2b4c9a6 --- /dev/null +++ b/source/lib/vc5_encoder/codebooks.c @@ -0,0 +1,422 @@ +/*! @file codebooks.c + * + * @brief Implementation of the routines for computing the encoding tables from a codebook. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" +#include "table17.inc" + +//! Length of the codebook for runs of zeros +#define RUNS_TABLE_LENGTH 3072 + + +/*! + @brief Define the codeset used by the reference codec + + The baseline codec only supports codebook #17. + + Codebook #17 is intended to be used with cubic companding + (see @ref FillMagnitudeEncodingTable and @ref ComputeCubicTable). + */ +ENCODER_CODESET encoder_codeset_17 = { + "Codebook set 17 from data by David Newman with tables automatically generated for the FSM decoder", + (const CODEBOOK *)&table17, + CODESET_FLAGS_COMPANDING_CUBIC, + NULL, + NULL, +}; + +/*! + @brief Initialize the codeset by creating more efficient tables for encoding + + This routine takes the original codebook in the codeset and creates a table + of codewords for runs of zeros, indexed by the run length, and a table for + coefficient magnitudes, indexed by the coefficient magnitude. This allows + runs of zeros and non-zero coefficients to be entropy coded using a simple + table lookup. +*/ +CODEC_ERROR PrepareCodebooks(const gpr_allocator *allocator, ENCODER_CODESET *cs) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Create a new table for runs of zeros with the default length + const int runs_table_length = RUNS_TABLE_LENGTH; + + // Initialize the indexable table of run length codes + RLV *old_codes = (RLV *)(((uint8_t *)cs->codebook) + sizeof(CODEBOOK)); + int old_length = cs->codebook->length; + + size_t runs_table_size = runs_table_length * sizeof(RLC) + sizeof(RUNS_TABLE); + + RUNS_TABLE *runs_table = allocator->Alloc(runs_table_size); + RLC *new_codes = (RLC *)(((uint8_t *)runs_table) + sizeof(RUNS_TABLE)); + int new_length = runs_table_length; + + // Set the length of the table for encoding coefficient magnitudes + int mags_table_shift = 8; + int mags_table_length; + size_t mags_table_size; + MAGS_TABLE *mags_table; + VLE *mags_table_entries; + + // Use a larger table if companding + if (CompandingParameter() > 0) { + //mags_table_shift = 11; + mags_table_shift = 10; + } + + // Allocate the table for encoding coefficient magnitudes + mags_table_length = (1 << mags_table_shift); + mags_table_size = mags_table_length * sizeof(VLE) + sizeof(MAGS_TABLE); + mags_table = allocator->Alloc(mags_table_size); + if (mags_table == NULL) { + return CODEC_ERROR_OUTOFMEMORY; + } + + mags_table_entries = (VLE *)(((uint8_t *)mags_table) + sizeof(MAGS_TABLE)); + + // Create a more efficient codebook for encoding runs of zeros + error = ComputeRunLengthCodeTable(allocator, + old_codes, old_length, new_codes, new_length); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Store the new table for runs of zeros in the codeset + runs_table->length = runs_table_length; + cs->runs_table = runs_table; + + error = FillMagnitudeEncodingTable(cs->codebook, mags_table_entries, mags_table_length, cs->flags); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + mags_table->length = mags_table_length; + cs->mags_table = mags_table; + + // The codebooks have been initialized successfully + return CODEC_ERROR_OKAY; +} + +/*! + @brief Free all data structures allocated for the codebooks +*/ +CODEC_ERROR ReleaseCodebooks(gpr_allocator *allocator, ENCODER_CODESET *cs) +{ + allocator->Free( (void *)cs->runs_table ); + allocator->Free( (void *)cs->mags_table); + return CODEC_ERROR_OKAY; +} + +/*! + @brief Compute a table of codewords for runs of zeros + + The table is indexed by the length of the run of zeros. +*/ +CODEC_ERROR ComputeRunLengthCodeTable(const gpr_allocator *allocator, + RLV *input_codes, int input_length, + RLC *output_codes, int output_length) +{ + // Need enough space for the codebook and the code for a single value + int runs_codebook_length = input_length + 1; + size_t runs_codebook_size = runs_codebook_length * sizeof(RLC); + RLC *runs_codebook = (RLC *)allocator->Alloc(runs_codebook_size); + bool single_zero_run_flag = false; + int input_index; + int runs_codebook_count = 0; + CODEC_ERROR return_code = CODEC_ERROR_OKAY; + + // Copy the codes for runs of zeros into the temporary codebook for sorting + for (input_index = 0; input_index < input_length; input_index++) + { + uint32_t count = input_codes[input_index].count; + int32_t value = input_codes[input_index].value; + + // Is this code for a run of zeros? + if (value != 0 || count == 0) { + // Skip codebook entries for coefficient magnitudes and special codes + continue; + } + + // Is this code for a single zero + if (count == 1 && value == 0) { + single_zero_run_flag = true; + } + + // Check that the temporary runs codebook is not full + assert(runs_codebook_count < runs_codebook_length); + + // Copy the code into the temporary runs codebook + runs_codebook[runs_codebook_count].size = input_codes[input_index].size; + runs_codebook[runs_codebook_count].bits = input_codes[input_index].bits; + runs_codebook[runs_codebook_count].count = count; + + // Check the codebook entry + assert(runs_codebook[runs_codebook_count].size > 0); + assert(runs_codebook[runs_codebook_count].count > 0); + + // Increment the count of codes in the temporary runs codebook + runs_codebook_count++; + } + + // Check that the runs codebook includes a run of a single zero + if( single_zero_run_flag == false ) + { + assert(false); + return_code = CODEC_ERROR_UNEXPECTED; + } + + // Sort the codewords into decreasing run length + SortDecreasingRunLength(runs_codebook, runs_codebook_count); + + // The last code must be for a single run + assert(runs_codebook[runs_codebook_count - 1].count == 1); + + // Fill the lookup table with codes for runs indexed by the run length + FillRunLengthEncodingTable(runs_codebook, runs_codebook_count, output_codes, output_length); + + // Free the space used for the sorted codewords + allocator->Free(runs_codebook); + + return return_code; +} + +/*! + @brief Sort the codebook into decreasing length of the run +*/ +CODEC_ERROR SortDecreasingRunLength(RLC *codebook, int length) +{ + int i; + int j; + + // Perform a simple bubble sort since the codebook may already be sorted + for (i = 0; i < length; i++) + { + for (j = i+1; j < length; j++) + { + // Should not have more than one codebook entry with the same run length + assert(codebook[i].count != codebook[j].count); + + // Exchange codebook entries if the current entry is smaller + if (codebook[i].count < codebook[j].count) + { + int size = codebook[i].size; + uint32_t bits = codebook[i].bits; + int count = codebook[i].count; + + codebook[i].size = codebook[j].size; + codebook[i].bits = codebook[j].bits; + codebook[i].count = codebook[j].count; + + codebook[j].size = size; + codebook[j].bits = bits; + codebook[j].count = count; + } + } + + // After two iterations that last two items should be in the proper order + assert(i == 0 || codebook[i-1].count > codebook[i].count); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Use a sparse run length code table to create an indexable table for faster encoding +*/ +CODEC_ERROR FillRunLengthEncodingTable(RLC *codebook, int codebook_length, RLC *table, int table_length) +{ + int i; // Index into the lookup table + int j; // Index into the codebook + + // Use all of the bits except the sign bit for the codewords + int max_code_size = bit_word_count - 1; + + // Check that the input codes are sorted into decreasing run length + for (j = 1; j < codebook_length; j++) + { + RLC *previous = &codebook[j-1]; + RLC *current = &codebook[j]; + + assert(previous->count > current->count); + if (! (previous->count > current->count)) { + return CODEC_ERROR_UNEXPECTED; + } + } + + // The last input code should be the code for a single zero + assert(codebook[codebook_length - 1].count == 1); + + // Create the shortest codeword for each table entry + for (i = 0; i < table_length; i++) + { + int length = i; // Length of the run for this table entry + uint32_t codeword = 0; // Composite codeword for this run length + int codesize = 0; // Number of bits in the composite codeword + int remaining; // Remaining run length not covered by the codeword + + remaining = length; + + for (j = 0; j < codebook_length; j++) + { + int repetition; // Number of times the codeword is used + int k; + + // Nothing to do if the remaining run length is zero + if (remaining == 0) break; + + // The number of times that the codeword is used is the number + // of times that it divides evenly into the remaining run length + repetition = remaining / codebook[j].count; + + // Append the codes to the end of the composite codeword + for (k = 0; k < repetition; k++) + { + // Terminate the loop if the codeword will not fit + if (codebook[j].size > (max_code_size - codesize)) + { + if (codesize) + { + remaining -= (k * codebook[j].count); + goto next; + } + else + { + break; + } + } + + // Shift the codeword to make room for the appended codes + codeword <<= codebook[j].size; + + // Insert the codeword from the codebook at the end of the composite codeword + codeword |= codebook[j].bits; + + // Increment the number of bits in the composite codeword + codesize += codebook[j].size; + } + + // Reduce the run length by the amount that was consumed by the repeated codeword + remaining -= (k * codebook[j].count); + } + +next: + // Should have covered the entire run if the last codeword would fit + //assert(remaining == 0 || (max_code_size - codesize) < codebook[codebook_length - 1].size); + + // Store the composite run length in the lookup table + table[i].bits = codeword; + table[i].size = codesize; + table[i].count = length - remaining; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Fill lookup table for encoding coefficient magnitudes + + The table for encoding coefficient magnitudes is indexed by the magnitude. + Each entry is a codeword and the size in bits. + + @todo Implement cubic companding +*/ +CODEC_ERROR FillMagnitudeEncodingTable(const CODEBOOK *codebook, VLE *mags_table_entry, int mags_table_length, uint32_t flags) +{ + // Get the length of the codebook and a pointer to the entries + //int codebook_length = codebook->length; + RLV *codebook_entry = (RLV *)((uint8_t *)codebook + sizeof(CODEBOOK)); + + int32_t maximum_magnitude_value = 0; + + uint32_t codebook_index; + + int16_t cubic_table[1025]; + int cubic_table_length = sizeof(cubic_table) / sizeof(cubic_table[0]); + + int mags_table_index; + + // Find the maximum coefficient magnitude in the codebook + for (codebook_index = 0; codebook_index < codebook->length; codebook_index++) + { + // Does this codebook entry correspond to a coefficient magnitude? + if (codebook_entry[codebook_index].count == 1) + { + int32_t codebook_value = codebook_entry[codebook_index].value; + if (maximum_magnitude_value < codebook_value) { + maximum_magnitude_value = codebook_value; + } + } + } + assert(maximum_magnitude_value > 0); + + if (flags & CODESET_FLAGS_COMPANDING_CUBIC) + { + ComputeCubicTable(cubic_table, cubic_table_length, maximum_magnitude_value); + } + + // Fill each table entry with the codeword for that (signed) value + for (mags_table_index = 0; mags_table_index < mags_table_length; mags_table_index++) + { + // Compute the magnitude that corresponds to this index + int32_t magnitude = mags_table_index; + uint32_t codeword; + int codesize; + + // Apply the companding curve + if (flags & CODESET_FLAGS_COMPANDING_CUBIC) + { + // Apply a cubic companding curve + assert(magnitude < cubic_table_length); + magnitude = cubic_table[magnitude]; + } + else if (flags & CODESET_FLAGS_COMPANDING_NONE) + { + // Do not apply a companding curve + } + else + { + // Apply an old-style companding curve + magnitude = CompandedValue(magnitude); + } + + // Is the magnitude larger than the number of entries in the codebook? + if (magnitude > maximum_magnitude_value) { + magnitude = maximum_magnitude_value; + } + + // Find the codebook entry corresponding to this coefficient magnitude + codeword = 0; + codesize = 0; + for (codebook_index = 0; codebook_index < codebook->length; codebook_index++) + { + if (codebook_entry[codebook_index].value == magnitude) + { + assert(codebook_entry[codebook_index].count == 1); + codeword = codebook_entry[codebook_index].bits; + codesize = codebook_entry[codebook_index].size; + break; + } + } + assert(0 < codesize && codesize <= 32); + + mags_table_entry[mags_table_index].bits = codeword; + mags_table_entry[mags_table_index].size = codesize; + + } + + return CODEC_ERROR_OKAY; +} diff --git a/source/lib/vc5_encoder/codebooks.h b/source/lib/vc5_encoder/codebooks.h new file mode 100755 index 0000000..dc8757a --- /dev/null +++ b/source/lib/vc5_encoder/codebooks.h @@ -0,0 +1,71 @@ +/*! @file codebooks.h + * + * @brief Declaration of the routines for computing the encoding tables from a codebook. + + * A codebooks contains the variable-length codes for coefficient magnitudes, runs + * of zeros, and special codewords that mark entropy codec locations in bitstream. + + * The codebook is used to create tables and simplify entropy coding of signed values + * and runs of zeros. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef CODEBOOKS_H +#define CODEBOOKS_H + + +#ifdef __cplusplus +extern "C" { +#endif + + /*! + Codeset data structure that is used by the encoder + */ + typedef struct encoder_codeset { + + const char *title; //!< Identifying string for the codeset + + const CODEBOOK *codebook; //!< Codebook for runs and magnitudes + + uint32_t flags; //!< Encoding flags (see the codeset flags) + + const MAGS_TABLE *mags_table; //!< Table for encoding coefficient magnitudes + + const RUNS_TABLE *runs_table; //!< Table for encoding runs of zeros + + } ENCODER_CODESET; + + extern ENCODER_CODESET encoder_codeset_17; + + CODEC_ERROR PrepareCodebooks(const gpr_allocator *allocator, ENCODER_CODESET *cs); + + CODEC_ERROR ReleaseCodebooks(gpr_allocator *allocator, ENCODER_CODESET *cs); + + CODEC_ERROR ComputeRunLengthCodeTable(const gpr_allocator *allocator, + RLV *input_codes, int input_length, + RLC *output_codes, int output_length); + + CODEC_ERROR SortDecreasingRunLength(RLC *codebook, int length); + + CODEC_ERROR FillRunLengthEncodingTable(RLC *codebook, int codebook_length, + RLC *table, int table_length); + + CODEC_ERROR FillMagnitudeEncodingTable(const CODEBOOK *codebook, VLE *table, int size, uint32_t flags); + +#ifdef __cplusplus +} +#endif + +#endif // CODEBOOKS_H diff --git a/source/lib/vc5_encoder/component.c b/source/lib/vc5_encoder/component.c new file mode 100755 index 0000000..da7d95b --- /dev/null +++ b/source/lib/vc5_encoder/component.c @@ -0,0 +1,404 @@ +/*! @file component.c + * + * @brief Implementation of the inverse component transform and inverse component permutation. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +/*! + @brief Initialize a component transform + */ +CODEC_ERROR InitComponentTransform(COMPONENT_TRANSFORM *transform) +{ + if (transform != NULL) + { + transform->component_count = 0; + transform->transform_matrix = NULL; + transform->transform_offset = NULL; + transform->transform_scale = NULL; + return CODEC_ERROR_OKAY; + } + return CODEC_ERROR_UNEXPECTED; +} + +/*! + @brief Initialize a component permutation + */ +CODEC_ERROR InitComponentPermutation(COMPONENT_PERMUTATION *permutation) +{ + if (permutation != NULL) + { + permutation->component_count = 0; + permutation->permutation_array = NULL; + return CODEC_ERROR_OKAY; + } + return CODEC_ERROR_UNEXPECTED; +} + +/*! + @brief Allocate the arrays in a component transform + + The allocated arrays in the component transform are initialized to all zeros. + */ +CODEC_ERROR AllocateComponentTransform(gpr_allocator *allocator, COMPONENT_TRANSFORM *transform, int component_count) +{ + size_t transform_matrix_size = component_count * component_count * sizeof(uint16_t); + size_t transform_offset_size = component_count * sizeof(uint16_t); + size_t transform_scale_size = component_count * sizeof(uint16_t); + + transform->transform_matrix = (uint16_t *)allocator->Alloc(transform_matrix_size); + transform->transform_offset = (uint16_t *)allocator->Alloc(transform_offset_size); + transform->transform_scale = (uint16_t *)allocator->Alloc(transform_scale_size); + + if (transform->transform_matrix == NULL || + transform->transform_offset == NULL || + transform->transform_scale == NULL) { + + //TODO: Should clean up the partially allocated transform arrays + return CODEC_ERROR_OUTOFMEMORY; + } + + transform->component_count = component_count; + + memset(transform->transform_matrix, 0, transform_matrix_size); + memset(transform->transform_offset, 0, transform_offset_size); + memset(transform->transform_scale, 0, transform_scale_size); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Allocate the arrays in a component permutation + + The allocated arrays in the component permutation are initialized to all zeros. + */ +CODEC_ERROR AllocateComponentPermutation(gpr_allocator *allocator, COMPONENT_PERMUTATION *permutation, int component_count) +{ + size_t permutation_array_size = component_count * sizeof(uint16_t); + + permutation->permutation_array = (uint16_t *)allocator->Alloc(permutation_array_size); + if (permutation->permutation_array == NULL) { + return CODEC_ERROR_OUTOFMEMORY; + } + + permutation->component_count = component_count; + + memset(permutation->permutation_array, 0, permutation_array_size); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Release the arrays in a component transform + */ +CODEC_ERROR ReleaseComponentTransform(gpr_allocator *allocator, COMPONENT_TRANSFORM *transform) +{ + if (transform != NULL) + { + allocator->Free(transform->transform_matrix); + allocator->Free(transform->transform_offset); + allocator->Free(transform->transform_scale); + memset(transform, 0, sizeof(COMPONENT_TRANSFORM)); + } + return CODEC_ERROR_OKAY; +} + +/*! + @brief Release the arrays in a component permutation + */ +CODEC_ERROR ReleaseComponentPermutation(gpr_allocator *allocator, COMPONENT_PERMUTATION *permutation) +{ + if (permutation != NULL) + { + allocator->Free(permutation->permutation_array); + memset(permutation, 0, sizeof(COMPONENT_PERMUTATION)); + } + return CODEC_ERROR_OKAY; +} + +/*! + @brief Initialize a component transform to the identity transform + */ +CODEC_ERROR InitComponentTransformIdentity(COMPONENT_TRANSFORM *transform, int component_count, gpr_allocator *allocator) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + int component_index; + + InitComponentTransform(transform); + error = AllocateComponentTransform(allocator, transform, component_count); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + for (component_index = 0; component_index < component_count; component_index++) + { + // Compute the index to the diagonal element in the matrix + int array_index = component_index * component_count + component_index; + transform->transform_matrix[array_index] = 1; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Initialize a component transform to the identity permutation + + */ +CODEC_ERROR InitComponentPermutationIdentity(COMPONENT_PERMUTATION *permutation, int component_count, gpr_allocator *allocator) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + int component_index; + + InitComponentPermutation(permutation); + error = AllocateComponentPermutation(allocator, permutation, component_count); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + for (component_index = 0; component_index < component_count; component_index++) + { + permutation->permutation_array[component_index] = component_index; + } + + return CODEC_ERROR_OKAY; +} + + +/*! + @brief Initialize a component transform to known values for testing + */ +CODEC_ERROR InitComponentTransformTesting(COMPONENT_TRANSFORM *transform, int component_count, gpr_allocator *allocator) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + int row; + int column; + + InitComponentTransform(transform); + error = AllocateComponentTransform(allocator, transform, component_count); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + for (row = 0; row < component_count; row++) + { + for (column = 0; column < component_count; column++) + { + // Compute the index to the element in the matrix + int array_index = row * component_count + column; + transform->transform_matrix[array_index] = array_index; + } + + transform->transform_offset[row] = (component_count - row); + transform->transform_scale[row] = row + 1; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Initialize a component transform to known values for testing + + */ +CODEC_ERROR InitComponentPermutationTesting(COMPONENT_PERMUTATION *permutation, int component_count, gpr_allocator *allocator) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + int component_index; + + InitComponentPermutation(permutation); + error = AllocateComponentPermutation(allocator, permutation, component_count); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + for (component_index = 0; component_index < component_count; component_index++) + { + permutation->permutation_array[component_index] = component_count - component_index - 1; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Return true if the component transform is the identity transform + */ +bool IsComponentTransformIdentity(COMPONENT_TRANSFORM *transform) +{ + int component_count; + int component_row; + int component_column; + + // A null transform is equivalent to the identity transform + if (transform == NULL) return true; + + component_count = transform->component_count; + + for (component_row = 0; component_row < component_count; component_row++) + { + // Is the transform matrix the identity matrix? + for (component_column = 0; component_column < component_count; component_column++) + { + // Compute the index to the component element in the transform matrix + int array_index = component_row * component_count + component_column; + + if (component_row == component_column) + { + if (transform->transform_matrix[array_index] != 1) return false; + } + else + { + if (transform->transform_matrix[array_index] != 0) return false; + } + } + + // Is the transform offset zero? + if (transform->transform_offset != 0) return false; + + // Is the scale factor zero? + if (transform->transform_scale != 0) return false; + } + + // The component transform is the identity transform + return true; +} + +/*! + @brief Allocate the arrays in a component permutation + */ +bool IsComponentPermutationIdentity(COMPONENT_PERMUTATION *permutation) +{ + int component_count; + int component_index; + + // A null permutation is equivalent to the identity permutation + if (permutation == NULL) { + return true; + } + + component_count = permutation->component_count; + + for (component_index = 0; component_index < component_count; component_index++) + { + if (permutation->permutation_array[component_index] != component_index) { + return false; + } + } + + // The component permutation is the identity permutation + return true; +} + +/*! + @brief Write the component transform to the bitstream + + @todo Use the InverseTransform16 syntax element if any values are larger than a single byte + */ +CODEC_ERROR WriteComponentTransform(COMPONENT_TRANSFORM *transform, BITSTREAM *stream) +{ + if (transform != NULL) + { + const int component_count = transform->component_count; + const size_t chunk_payload_size = (component_count * component_count + 2 * component_count) * sizeof(uint8_t); + const size_t chunk_payload_padding = sizeof(SEGMENT) - (chunk_payload_size % sizeof(SEGMENT)); + const int chunk_payload_length = (int)((chunk_payload_size + sizeof(SEGMENT) - 1) / sizeof(SEGMENT)); + int i; + + // Write the tag value pair for the small chunk element for the component transform + PutTagPair(stream, CODEC_TAG_InverseTransform, chunk_payload_length); + + for (i = 0; i < component_count; i++) + { + int offset_value; + int scale_value; + + // Write the row at this index in the transform matrix + int j; + + for (j = 0; j < component_count; j++) + { + int array_index = i * component_count + j; + int array_value = transform->transform_matrix[array_index]; + + assert(INT8_MIN <= array_value && array_value <= INT8_MAX); + PutBits(stream, array_value, 8); + } + + // Write the offset + offset_value = transform->transform_offset[i]; + assert(INT8_MIN <= offset_value && offset_value <= INT8_MAX); + PutBits(stream, offset_value, 8); + + // Write the scale + scale_value = transform->transform_scale[i]; + assert(0 <= scale_value && scale_value <= UINT8_MAX); + PutBits(stream, scale_value, 8); + } + + // Pad the remainer of the chunk payload with zeros + for (i = 0; i < (int)chunk_payload_padding; i++) { + PutBits(stream, 0, 8); + } + + // Check that the bitstream is aligned on a segment boundary + assert(IsAlignedSegment(stream)); + if (! (IsAlignedSegment(stream))) { + return CODEC_ERROR_UNEXPECTED; + } + + return CODEC_ERROR_OKAY; + } + + return CODEC_ERROR_UNEXPECTED; +} + +/*! + @brief Write the component permutation to the bitstream + */ +CODEC_ERROR WriteComponentPermutation(COMPONENT_PERMUTATION *permutation, BITSTREAM *stream) +{ + if (permutation != NULL) + { + const int component_count = permutation->component_count; + const size_t chunk_payload_size = component_count * sizeof(uint8_t); + const size_t chunk_payload_padding = sizeof(SEGMENT) - (chunk_payload_size % sizeof(SEGMENT)); + const int chunk_payload_length = (int)((chunk_payload_size + sizeof(SEGMENT) - 1) / sizeof(SEGMENT)); + int i; + + // Write the tag value pair for the small chunk element for the component transform + PutTagPair(stream, CODEC_TAG_InversePermutation, chunk_payload_length); + + for (i = 0; i < component_count; i++) + { + uint8_t value = (uint8_t)permutation->permutation_array[i]; + assert(0 <= value && value <= UINT8_MAX); + PutBits(stream, value, 8); + } + + // Pad the remainer of the chunk payload with zeros + for (i = 0; i < (int)chunk_payload_padding; i++) { + PutBits(stream, 0, 8); + } + + // Check that the bitstream is aligned on a segment boundary + assert(IsAlignedSegment(stream)); + if (! (IsAlignedSegment(stream))) { + return CODEC_ERROR_UNEXPECTED; + } + + return CODEC_ERROR_OKAY; + } + + return CODEC_ERROR_UNEXPECTED; +} diff --git a/source/lib/vc5_encoder/component.h b/source/lib/vc5_encoder/component.h new file mode 100755 index 0000000..1c194c4 --- /dev/null +++ b/source/lib/vc5_encoder/component.h @@ -0,0 +1,100 @@ +/*! @file component.h + * + * @brief Declaration of routines for the inverse component transform and permutation. + + * A codebooks contains the variable-length codes for coefficient magnitudes, runs + * of zeros, and special codewords that mark entropy codec locations in bitstream. + + * The codebook is used to create tables and simplify entropy coding of signed values + * and runs of zeros. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef COMPONENT_H +#define COMPONENT_H + +/*! + @brief Data structure for the component transform (16 bit precision) + */ +typedef struct _component_transform +{ + uint16_t component_count; + uint16_t *transform_matrix; + uint16_t *transform_offset; + uint16_t *transform_scale; + +} COMPONENT_TRANSFORM; + +/*! + @brief Data structure for the component permutation + */ +typedef struct _component_permutation +{ + uint16_t component_count; + uint16_t *permutation_array; + +} COMPONENT_PERMUTATION; + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR InitComponentTransform(COMPONENT_TRANSFORM *transform); + + CODEC_ERROR InitComponentPermutation(COMPONENT_PERMUTATION *permutation); + + CODEC_ERROR AllocateComponentTransform(gpr_allocator *allocator, + COMPONENT_TRANSFORM *transform, + int component_count); + + CODEC_ERROR AllocateComponentPermutation(gpr_allocator *allocator, + COMPONENT_PERMUTATION *permutation, + int component_count); + + CODEC_ERROR ReleaseComponentTransform(gpr_allocator *allocator, + COMPONENT_TRANSFORM *transform); + + CODEC_ERROR ReleaseComponentPermutation(gpr_allocator *allocator, + COMPONENT_PERMUTATION *permutation); + + CODEC_ERROR InitComponentTransformIdentity(COMPONENT_TRANSFORM *transform, + int component_count, + gpr_allocator *allocator); + + CODEC_ERROR InitComponentPermutationIdentity(COMPONENT_PERMUTATION *permutation, + int component_count, + gpr_allocator *allocator); + + CODEC_ERROR InitComponentTransformTesting(COMPONENT_TRANSFORM *transform, + int component_count, + gpr_allocator *allocator); + + CODEC_ERROR InitComponentPermutationTesting(COMPONENT_PERMUTATION *permutation, + int component_count, + gpr_allocator *allocator); + + bool IsComponentTransformIdentity(COMPONENT_TRANSFORM *transform); + + bool IsComponentPermutationIdentity(COMPONENT_PERMUTATION *permutation); + + CODEC_ERROR WriteComponentTransform(COMPONENT_TRANSFORM *transform, BITSTREAM *stream); + + CODEC_ERROR WriteComponentPermutation(COMPONENT_PERMUTATION *permutation, BITSTREAM *stream); + +#ifdef __cplusplus +} +#endif + +#endif // COMPONENT_H diff --git a/source/lib/vc5_encoder/encoder.c b/source/lib/vc5_encoder/encoder.c new file mode 100755 index 0000000..7495374 --- /dev/null +++ b/source/lib/vc5_encoder/encoder.c @@ -0,0 +1,2555 @@ +/*! @file encoder.c + * + * @brief Implementation of functions for encoding samples. + * + * Encoded samples must be aligned on a four byte boundary. + * Any constraints on the alignment of data within the sample + * are handled by padding the sample to the correct alignment. + * + * Note that the encoded dimensions are the actual dimensions of each channel + * (or the first channel in the case of 4:2:2 sampling) in the encoded sample. + * The display offsets and dimensions specify the portion of the encoded frame + * that should be displayed, but in the case of a Bayer image the display + * dimensions are doubled to account for the effects of the demosaic filter. + * If a Bayer image is encoded to Bayer format (no demosaic filter applied), + * then the encoded dimensions will be the same as grid of Bayer quads, less + * any padding required during encoding, but the display dimensions and + * offset will be reported as if a demosiac filter were applied to scale the + * encoded frame to the display dimensions (doubling the width and height). + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +#if ENABLED(NEON) +#include +#endif + +/*! + @brief Align the bitstream to a byte boundary + + Enough bits are written to the bitstream to align the + bitstream to the next byte. + */ +static CODEC_ERROR AlignBitsByte(BITSTREAM *bitstream) +{ + if (bitstream->count > 0 && (bitstream->count % 8) != 0) + { + // Compute the number of bits of padding + BITCOUNT count = (8 - (bitstream->count % 8)); + PutBits(bitstream, 0, count); + } + assert((bitstream->count % 8) == 0); + return CODEC_ERROR_OKAY; +} + +/*! + @brief Align the bitstream to the next segment + + The corresponding function in the existing codec flushes the bitstream. + + @todo Is it necessary to flush the bitstream (and the associated byte stream) + after aligning the bitstream to a segment boundary? + */ +static CODEC_ERROR AlignBitsSegment(BITSTREAM *bitstream) +{ + STREAM *stream = bitstream->stream; + size_t byte_count; + + // Byte align the bitstream + AlignBitsByte(bitstream); + assert((bitstream->count % 8) == 0); + + // Compute the number of bytes in the bit buffer + byte_count = bitstream->count / 8; + + // Add the number of bytes written to the stream + byte_count += stream->byte_count; + + while ((byte_count % sizeof(TAGVALUE)) != 0) + { + PutBits(bitstream, 0, 8); + byte_count++; + } + + // The bitstream should be aligned to the next segment + assert((bitstream->count == 0) || (bitstream->count == bit_word_count)); + assert((byte_count % sizeof(TAGVALUE)) == 0); + + return CODEC_ERROR_OKAY; +} + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) +/*! + @brief Set default values for the pattern element structure + + Some image formats imply specific parameters for the dimensions of the + pattern elements and the number of components per sample. If the pattern + element structure has not been fully specified by the command-line + arguments, then missing values can be filled in from the default values + for the image format. + */ +bool SetImageFormatDefaults(ENCODER *encoder) +{ + switch (encoder->image_format) + { +#if VC5_ENABLED_PART(VC5_PART_COLOR_SAMPLING) + if (IsPartEnabled(encoder->enabled_parts, VC5_PART_COLOR_SAMPLING)) + { + // The components per sample parameter is not applicable to VC-5 Part 4 bitstreams + assert(encoder->components_per_sample == 0); + encoder->components_per_sample = 0; + } + else + { + // Set the default components per sample assuming no alpha channel + if (encoder->components_per_sample == 0) { + encoder->components_per_sample = 3; + } + } +#else + // Set the default components per sample assuming no alpha channel + if (encoder->components_per_sample == 0) { + encoder->components_per_sample = 3; + } +#endif + return true; + + case IMAGE_FORMAT_RAW: + if (encoder->pattern_width == 0) { + encoder->pattern_width = 2; + } + + if (encoder->pattern_height == 0) { + encoder->pattern_height = 2; + } + + if (encoder->components_per_sample == 0) { + encoder->components_per_sample = 1; + } + + return true; + + default: + // Unable to set default values for the pattern elements + return false; + } +} +#endif + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) +/*! + @brief Check for inconsistent values for the parameters specified on the command-line + + This routine looks for inconsistencies between the image format, the dimensions of the + pattern elements, and the number of components per sample. + */ +bool CheckImageFormatParameters(ENCODER *encoder) +{ + switch (encoder->image_format) + { + case IMAGE_FORMAT_RAW: + + if (encoder->pattern_width != 2) { + return false; + } + + if (encoder->pattern_height != 2) { + return false; + } + + if (encoder->components_per_sample != 1) { + return false; + } + + // The parameters for the Bayer image format are correct + return true; + + default: + // Cannot verify the parameters for an unknown image format + return false; + + } +} +#endif + +/*! + @brief Prepare the encoder state +*/ +CODEC_ERROR PrepareEncoderState(ENCODER *encoder, + const UNPACKED_IMAGE *image, + const ENCODER_PARAMETERS *parameters) +{ + CODEC_STATE *codec = &encoder->codec; + int channel_count = image->component_count; + int channel_number; + + // Set the default value for the number of bits per lowpass coefficient + PRECISION lowpass_precision = 16; + + if (parameters->encoded.lowpass_precision > 0) { + lowpass_precision = parameters->encoded.lowpass_precision; + } + + for (channel_number = 0; channel_number < channel_count; channel_number++) + { + DIMENSION width = image->component_array_list[channel_number].width; + DIMENSION height = image->component_array_list[channel_number].height; + PRECISION bits_per_component = image->component_array_list[channel_number].bits_per_component; + + // Copy the component array parameters into the encoder state + encoder->channel[channel_number].width = width; + encoder->channel[channel_number].height = height; + encoder->channel[channel_number].bits_per_component = bits_per_component; + + // The lowpass bands in all channels are encoded with the same precision + encoder->channel[channel_number].lowpass_precision = lowpass_precision; + } + + // Record the number of channels in the encoder state + encoder->channel_count = channel_count; + + // The encoder uses three wavelet transform levels for each channel + encoder->wavelet_count = 3; + + // Set the channel encoding order + if (parameters->channel_order_count > 0) + { + // Use the channel order specified by the encoding parameters + encoder->channel_order_count = parameters->channel_order_count; + memcpy(encoder->channel_order_table, parameters->channel_order_table, sizeof(encoder->channel_order_table)); + } + else + { + // Use the default channel encoding order + for (channel_number = 0; channel_number < channel_count; channel_number++) + { + encoder->channel_order_table[channel_number] = channel_number; + } + encoder->channel_order_count = channel_count; + } + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + // The actual image dimensions are reported in the bitstream header (VC-5 Part 3) + encoder->image_width = parameters->input.width; + encoder->image_height = parameters->input.height; + encoder->pattern_width = parameters->pattern_width; + encoder->pattern_height = parameters->pattern_height; + encoder->components_per_sample = parameters->components_per_sample; + encoder->image_format = parameters->encoded.format; + encoder->max_bits_per_component = MaxBitsPerComponent(image); + + // Set default parameters for the image format + SetImageFormatDefaults(encoder); + + if (!CheckImageFormatParameters(encoder)) { + return CODEC_ERROR_BAD_IMAGE_FORMAT; + } +#else + // The dimensions of the image is the maximum of the channel dimensions (VC-5 Part 1) + GetMaximumChannelDimensions(image, &encoder->image_width, &encoder->image_height); +#endif + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + // Interlaced images are encoded as separate layers + encoder->progressive = parameters->progressive; + encoder->top_field_first = TRUE; + encoder->frame_inverted = FALSE; + encoder->progressive = 1; + + // Set the number of layers (sub-samples) in the encoded sample + encoder->layer_count = parameters->layer_count; +#endif + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + // by default, all sections are enabled + encoder->enabled_sections = parameters->enabled_sections; +#endif + + // Initialize the codec state with the default parameters used by the decoding process + return PrepareCodecState(codec); +} + +/*! + @brief Initialize the encoder data structure + + This routine performs the same function as a C++ constructor. + The encoder is initialized with default values that are replaced + by the parameters used to prepare the encoder (see @ref PrepareEncoder). + + This routine does not perform all of the initializations required + to prepare the encoder data structure for decoding a sample. +*/ +CODEC_ERROR InitEncoder(ENCODER *encoder, const gpr_allocator *allocator, const VERSION *version) +{ + assert(encoder != NULL); + if (! (encoder != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + memset(encoder, 0, sizeof(ENCODER)); + + // Assign a memory allocator to the encoder + encoder->allocator = (gpr_allocator *)allocator; + + // Write debugging information to standard output + encoder->logfile = stdout; + + if (version) + { + // Store the version number in the encoder + memcpy(&encoder->version, version, sizeof(encoder->version)); + } + else + { + // Clear the version number in the encoder + memset(&encoder->version, 0, sizeof(encoder->version)); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Encode the image into the output stream + + This is a convenience routine for applications that use a byte stream to + represent a memory buffer or binary file that will store the encoded image. + + The image is unpacked into a set of component arrays by the image unpacking + process invoked by calling the routine @ref ImageUnpackingProcess. The image + unpacking process is informative and is not part of the VC-5 standard. + + The main entry point for encoding the component arrays output by the image + unpacking process is @ref EncodingProcess. +*/ +CODEC_ERROR EncodeImage(IMAGE *image, STREAM *stream, RGB_IMAGE *rgb_image, ENCODER_PARAMETERS *parameters) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Allocate data structures for the encoder state and the bitstream + ENCODER encoder; + BITSTREAM bitstream; + + SetupEncoderLogCurve(); + + UNPACKED_IMAGE unpacked_image; + + // Unpack the image into a set of component arrays + error = ImageUnpackingProcess(image, &unpacked_image, parameters, ¶meters->allocator); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Initialize the bitstream data structure + InitBitstream(&bitstream); + + // Bind the bitstream to the byte stream + error = AttachBitstream(&bitstream, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Encode the component arrays into the bitstream + error = EncodingProcess(&encoder, &unpacked_image, &bitstream, parameters); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + if( rgb_image != NULL && parameters->rgb_resolution == GPR_RGB_RESOLUTION_SIXTEENTH ) + { // Thumbnail + SetupDecoderLogCurve(); + + WaveletToRGB(parameters->allocator, + encoder.transform[0].wavelet[2]->data[LL_BAND], encoder.transform[1].wavelet[2]->data[LL_BAND], encoder.transform[2].wavelet[2]->data[LL_BAND], + encoder.transform[0].wavelet[2]->width, encoder.transform[0].wavelet[2]->height, encoder.transform[0].wavelet[2]->width, + rgb_image, 14, 8, ¶meters->rgb_gain ); + } + + error = ReleaseComponentArrays( ¶meters->allocator, &unpacked_image, unpacked_image.component_count ); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Release any resources allocated by the bitstream + ReleaseBitstream(&bitstream); + + // Release any resources allocated by the encoder + ReleaseEncoder(&encoder); + + return error; +} + +/*! + @brief Reference implementation of the VC-5 encoding process. + + The encoder takes input image in the form of a list of component arrays + produced by the image unpacking process and encodes the image into the + bitstream. + + External parameters are used to initialize the encoder state. + + The encoder state determines how the image is encoded int the bitstream. +*/ +CODEC_ERROR EncodingProcess(ENCODER *encoder, + const UNPACKED_IMAGE *image, + BITSTREAM *bitstream, + const ENCODER_PARAMETERS *parameters) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Initialize the encoder using the parameters provided by the application + error = PrepareEncoder(encoder, image, parameters); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + if (encoder->image_format == IMAGE_FORMAT_UNKNOWN) { + return CODEC_ERROR_BAD_IMAGE_FORMAT; + } + if ( parameters->verbose_flag ) + { + LogPrint("Pattern width: %d\n", encoder->pattern_width); + LogPrint("Pattern height: %d\n", encoder->pattern_height); + + if (!IsPartEnabled(encoder->enabled_parts, VC5_PART_COLOR_SAMPLING)) { + LogPrint("Components per sample: %d\n", encoder->components_per_sample); + } + LogPrint("Internal precision: %d\n", encoder->internal_precision); + + LogPrint("\n"); + } +#endif + + // Write the bitstream start marker + PutBitstreamStartMarker(bitstream); + + // Allocate six pairs of lowpass and highpass buffers for each channel + AllocateEncoderHorizontalBuffers(encoder); + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + + if (!IsPartEnabled(encoder.enabled_parts, VC5_PART_LAYERS) || encoder.layer_count == 1) + { + // Encode the image as a single layer in the sample + error = EncodeSingleImage(encoder, ImageData(image), image->pitch, &bitstream); + } + else + { + // Each layer encodes a separate frame + IMAGE *image_array[MAX_LAYER_COUNT]; + memset(image_array, 0, sizeof(image_array)); + + // The encoding parameters must include a decompositor + assert (parameters->decompositor != NULL); + + // Decompose the frame into individual frames for each layer + error = parameters->decompositor(image, image_array, encoder.layer_count); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Encode each frame as a separate layer in the sample + error = EncodeMultipleImages(encoder, image_array, encoder.layer_count, &bitstream); + } +#else + + // Encode one image into the bitstream + error = EncodeSingleImage(encoder, image, bitstream); + +#endif + + DeallocateEncoderHorizontalBuffers(encoder); + + return error; +} + +/*! + @brief Initialize the encoder using the specified parameters + + It is important to use the correct encoded image dimensions (including padding) + and the correct encoded format to initialize the encoder. The decoded image + dimensions must be adjusted to account for a lower decoded resolution if applicable. + + It is expected that the parameters data structure may change over time + with additional or different fields, depending on the codec profile or + changes made to the codec during further development. The parameters + data structure may have a version number or may evolve into a dictionary + of key-value pairs with missing keys indicating that a default value + should be used. + + @todo Add more error checking to this top-level routine +*/ +CODEC_ERROR PrepareEncoder(ENCODER *encoder, + const UNPACKED_IMAGE *image, + const ENCODER_PARAMETERS *parameters) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + VERSION version = VERSION_INITIALIZER(VC5_VERSION_MAJOR, VC5_VERSION_MINOR, VC5_VERSION_REVISION, 0); + PRECISION max_bits_per_component = MaxBitsPerComponent(image); + + // Initialize the encoder data structure + InitEncoder(encoder, ¶meters->allocator, &version); + + // Set the mask that specifies which parts of the VC-5 standard are supported + encoder->enabled_parts = parameters->enabled_parts; + + // Verify that the enabled parts are correct + error = VerifyEnabledParts(encoder->enabled_parts); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + + // Remember the internal precision used by the image unpacking process + encoder->internal_precision = minimum(max_bits_per_component, default_internal_precision); + + // Initialize the encoding parameters and the codec state + PrepareEncoderState(encoder, image, parameters); + + // Allocate the wavelet transforms + AllocEncoderTransforms(encoder); + + // Initialize the quantizer + SetEncoderQuantization(encoder, parameters); + + // Initialize the wavelet transforms + PrepareEncoderTransforms(encoder); + + // Allocate the scratch buffers used for encoding + AllocEncoderBuffers(encoder); + + // Initialize the encoding tables for magnitudes and runs of zeros + error = PrepareCodebooks(¶meters->allocator, &encoder_codeset_17 ); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Select the codebook for encoding + encoder->codeset = &encoder_codeset_17; + + // The encoder is ready to decode a sample + return CODEC_ERROR_OKAY; +} + +/*! + @brief Free all resources allocated by the encoder +*/ +CODEC_ERROR ReleaseEncoder(ENCODER *encoder) +{ + if (encoder != NULL) + { + gpr_allocator *allocator = encoder->allocator; + int channel; + + // Free the encoding tables + ReleaseCodebooks(allocator, encoder->codeset); + + // Free the wavelet tree for each channel + for (channel = 0; channel < MAX_CHANNEL_COUNT; channel++) + { + ReleaseTransform(allocator, &encoder->transform[channel]); + } + + //TODO: Free the encoding buffers + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Encode a single image into the bitstream + + This is the main entry point for encoding a single image into the bitstream. + The encoder must have been initialized by a call to @ref PrepareEncoder. + + The unpacked image is the set of component arrays output by the image unpacking + process. The bitstream must be initialized and bound to a byte stream before + calling this routine. +*/ +CODEC_ERROR EncodeSingleImage(ENCODER *encoder, const UNPACKED_IMAGE *image, BITSTREAM *stream) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Write the sample header that is common to all layers + error = EncodeBitstreamHeader(encoder, stream); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + + // Write the sample extension header to the bitstream + error = EncodeExtensionHeader(encoder, stream); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + + // Encode each component array as a separate channel in the bitstream + error = EncodeMultipleChannels(encoder, image, stream); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + + // Finish the encoded sample after the last layer + error = EncodeBitstreamTrailer(encoder, stream); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + + // Force any data remaining in the bitstream to be written into the sample + FlushBitstream(stream); + + return error; +} + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) +/*! + @brief Encode multiple frames as separate layers in a sample + + The encoder must have been initialized by a call to @ref PrepareEncoder. + + The bitstream must be initialized and bound to a byte stream before + calling this routine. +*/ +CODEC_ERROR EncodeMultipleFrames(ENCODER *encoder, IMAGE *image_array[], int frame_count, BITSTREAM *stream) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + //CODEC_STATE *codec = &encoder->codec; + + int layer_index; + + // The number of frames must match the number of layers in the sample + assert(frame_count == encoder->layer_count); + + // Initialize the codec state + PrepareEncoderState(encoder); + + // Write the bitstream start marker + error = PutBitstreamStartMarker(stream); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + + // Write the bitstream header that is common to all layers + error = EncodeBitstreamHeader(encoder, stream); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + + // Write the extension header to the bitstream + error = EncodeExtensionHeader(encoder, stream); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + + // Encode each frame in a separate layer in the sample + for (layer_index = 0; layer_index < frame_count; layer_index++) + { + error = EncodeLayer(encoder, image_array[layer_index]->buffer, image_array[layer_index]->pitch, stream); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + } + + error = EncodeSampleExtensionTrailer(encoder, stream); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + + // Finish the encoded sample after the last layer + error = EncodeSampleTrailer(encoder, stream); + assert(error == CODEC_ERROR_OKAY); + if (! (error == CODEC_ERROR_OKAY)) { + return error; + } + + // Force any data remaining in the bitstream to be written into the sample + FlushBitstream(stream); + + // Check that the sample offset stack has been emptied + assert(stream->sample_offset_count == 0); + + //TODO: Any resources need to be released? + + // Done encoding all layers in the sample + return error; +} +#endif + +/*! + @brief Initialize the wavelet transforms for encoding +*/ +CODEC_ERROR PrepareEncoderTransforms(ENCODER *encoder) +{ + //int channel_count = encoder->channel_count; + int channel_number; + + // Set the prescale and quantization in each wavelet transform + for (channel_number = 0; channel_number < encoder->channel_count; channel_number++) + { + TRANSFORM *transform = &encoder->transform[channel_number]; + + // Set the prescaling (may be used in setting the quantization) + int bits_per_component = encoder->channel[channel_number].bits_per_component; + SetTransformPrescale(transform, bits_per_component); + + //TODO: Are the wavelet scale factors still used? + + // Must set the transform scale if not calling SetTransformQuantization + SetTransformScale(transform); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Unpack the image into component arrays for encoding +*/ +CODEC_ERROR ImageUnpackingProcess(const PACKED_IMAGE *input, + UNPACKED_IMAGE *output, + const ENCODER_PARAMETERS *parameters, + gpr_allocator *allocator) +{ + ENABLED_PARTS enabled_parts = parameters->enabled_parts; + int channel_count; + DIMENSION max_channel_width; + DIMENSION max_channel_height; + int bits_per_component; + + // The configuration of component arrays is determined by the image format + switch (input->format) + { + case PIXEL_FORMAT_RAW_RGGB_12: + case PIXEL_FORMAT_RAW_RGGB_12P: + case PIXEL_FORMAT_RAW_RGGB_14: + case PIXEL_FORMAT_RAW_GBRG_12: + case PIXEL_FORMAT_RAW_GBRG_12P: + case PIXEL_FORMAT_RAW_RGGB_16: + channel_count = 4; + max_channel_width = input->width / 2; + max_channel_height = input->height / 2; + bits_per_component = 12; + break; + + default: + assert(0); + return CODEC_ERROR_PIXEL_FORMAT; + break; + } + + // Allocate space for the component arrays + AllocateComponentArrays(allocator, output, channel_count, max_channel_width, max_channel_height, + input->format, bits_per_component); + + + // The configuration of component arrays is determined by the image format + switch (input->format) + { + case PIXEL_FORMAT_RAW_RGGB_14: + UnpackImage_14(input, output, enabled_parts, true ); + break; + + case PIXEL_FORMAT_RAW_RGGB_12: + UnpackImage_12(input, output, enabled_parts, true ); + break; + + case PIXEL_FORMAT_RAW_GBRG_12: + UnpackImage_12(input, output, enabled_parts, false ); + break; + + case PIXEL_FORMAT_RAW_RGGB_12P: + UnpackImage_12P(input, output, enabled_parts, true ); + break; + + case PIXEL_FORMAT_RAW_GBRG_12P: + UnpackImage_12P(input, output, enabled_parts, false ); + break; + + default: + assert(0); + return CODEC_ERROR_PIXEL_FORMAT; + break; + } + + return CODEC_ERROR_OKAY; +} + + + +/*! + @brief Insert the header segments that are common to all samples + + This code was derived from PutVideoIntraFrameHeader in the current codec. + + @todo Need to output the channel size table. +*/ +CODEC_ERROR EncodeBitstreamHeader(ENCODER *encoder, BITSTREAM *stream) +{ + CODEC_STATE *codec = &encoder->codec; + + //TAGWORD subband_count = 10; + TAGWORD image_width = encoder->image_width; + TAGWORD image_height = encoder->image_height; + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + TAGWORD image_format = encoder->image_format; + TAGWORD pattern_width = encoder->pattern_width; + TAGWORD pattern_height = encoder->pattern_height; + TAGWORD components_per_sample = encoder->components_per_sample; + TAGWORD max_bits_per_component = encoder->max_bits_per_component; + TAGWORD default_bits_per_component = max_bits_per_component; +#else + TAGWORD default_bits_per_component = encoder->internal_precision; +#endif + + // Align the start of the header on a segment boundary + AlignBitsSegment(stream); + + // The bitstream should be aligned to a segment boundary + assert(IsAlignedSegment(stream)); + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (IsSectionEnabled(encoder, SECTION_NUMBER_HEADER)) + { + // Write the section header for the bitstream header into the bitstream + BeginHeaderSection(encoder, stream); + } +#endif + + // Output the number of channels + if (encoder->channel_count != codec->channel_count) { + PutTagPair(stream, CODEC_TAG_ChannelCount, encoder->channel_count); + codec->channel_count = encoder->channel_count; + } + + // Inform the decoder of the maximum component array dimensions + PutTagPair(stream, CODEC_TAG_ImageWidth, image_width); + PutTagPair(stream, CODEC_TAG_ImageHeight, image_height); + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + if (IsPartEnabled(encoder->enabled_parts, VC5_PART_IMAGE_FORMATS)) + { + PutTagPair(stream, CODEC_TAG_ImageFormat, image_format); + PutTagPair(stream, CODEC_TAG_PatternWidth, pattern_width); + PutTagPair(stream, CODEC_TAG_PatternHeight, pattern_height); + PutTagPair(stream, CODEC_TAG_ComponentsPerSample, components_per_sample); + PutTagPair(stream, CODEC_TAG_MaxBitsPerComponent, max_bits_per_component); + } +#endif + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + if (IsPartEnabled(encoder->enabled_parts, VC5_PART_LAYERS)) + { + // Output the number of layers in the sample (optional for backward compatibility) + //PutTagPairOptional(stream, CODEC_TAG_LAYER_COUNT, layer_count); + } +#endif + + // Record the image dimensions in the codec state + codec->image_width = image_width; + codec->image_height = image_height; + + // The image dimensions determine the default channel dimensions + codec->channel_width = image_width; + codec->channel_height = image_height; + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + if (IsPartEnabled(encoder->enabled_parts, VC5_PART_IMAGE_FORMATS)) + { + // Record the pattern element parameters in the codec state + codec->image_format = image_format; + codec->pattern_width = pattern_width; + codec->pattern_height = pattern_height; + codec->components_per_sample = components_per_sample; + codec->max_bits_per_component = (PRECISION)max_bits_per_component; + } +#endif + + // This parameter is the default precision for each channel + codec->bits_per_component = default_bits_per_component; + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (IsSectionEnabled(encoder, SECTION_NUMBER_HEADER)) + { + // Make sure that the bitstream is aligned to a segment boundary + AlignBitsSegment(stream); + + // Update the section header with the actual size of the bitstream header section + EndSection(stream); + } +#endif + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write the trailer at the end of the encoded sample + + This routine updates the sample size segment in the sample extension header + with the actual size of the encoded sample. The size of the encoded sample + does not include the size of the sample header or trailer. + + Note that the trailer may not be necessary as the decoder may stop + reading from the sample after it has decoded all of the information + required to reconstruct the frame. + + This code was derived from PutVideoIntraFrameTrailer in the current codec. +*/ +CODEC_ERROR EncodeBitstreamTrailer(ENCODER *encoder, BITSTREAM *stream) +{ + AlignBitsSegment(stream); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write the unique image identifier + + @todo Should the UMID instance number be a parameter to this routine? + */ +static CODEC_ERROR WriteUniqueImageIdentifier(ENCODER *encoder, BITSTREAM *stream) +{ + const int UMID_length_byte = 0x13; + const int UMID_instance_number = 0; + + // Total length of the unique image identifier chunk payload (in segments) + const int identifier_chunk_payload_length = UMID_length + sequence_number_length; + + // Write the tag value pair for the small chunk element for the unique image identifier + PutTagPairOptional(stream, CODEC_TAG_UniqueImageIdentifier, identifier_chunk_payload_length); + + // Write the UMID label + PutByteArray(stream, UMID_label, sizeof(UMID_label)); + + // Write the UMID length byte + PutBits(stream, UMID_length_byte, 8); + + // Write the UMID instance number + PutBits(stream, UMID_instance_number, 24); + + // Write the image sequence identifier + PutByteArray(stream, encoder->image_sequence_identifier, sizeof(encoder->image_sequence_identifier)); + + // Write the image sequence number + PutLong(stream, encoder->image_sequence_number); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write extra information that follows the sample header into the bitstream + + This routine writes metadata into the sample header extension. + + Metadata includes the unique GUID for each video clip, the number of each video frame, + and the timecode (if available). The GUID and frame number pair uniquely identify each + frame in the encoded clip. + + This routine also outputs additional information that describes the characterstics of + the encoded video in the GOP extension and sample flags. + + The size of the sample extension header is provided by the sample size segment. +*/ +CODEC_ERROR EncodeExtensionHeader(ENCODER *encoder, BITSTREAM *stream) +{ + ENABLED_PARTS enabled_parts = encoder->enabled_parts; + + // Encode the transform prescale for the first channel (assume all channels are the same) + TAGWORD prescale_shift = PackTransformPrescale(&encoder->transform[0]); + + // The tag-value pair is required if the encoder is not using the default values + //if (IsTransformPrescaleDefault(&encoder->transform[0], TRANSFORM_TYPE_SPATIAL, encoder->encoded.precision)) + if (IsTransformPrescaleDefault(&encoder->transform[0], encoder->internal_precision)) + { + PutTagPairOptional(stream, CODEC_TAG_PrescaleShift, prescale_shift); + } + else + { + PutTagPair(stream, CODEC_TAG_PrescaleShift, prescale_shift); + } + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS)) + { + WriteUniqueImageIdentifier(encoder, stream); + } +#endif + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS) && + !IsComponentTransformIdentity(encoder->component_transform)) + { + WriteComponentTransform(encoder->component_transform, stream); + } +#endif + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + if (IsPartEnabled(enabled_parts, VC5_PART_IMAGE_FORMATS) && + !IsComponentPermutationIdentity(encoder->component_permutation)) + { + WriteComponentPermutation(encoder->component_permutation, stream); + } +#endif + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write the sample extension trailer into the bitstream + + This routine must be called after encoding the sample and before writing the + sample trailer, but must only be called if the sample extension header was + written into the bitstream. +*/ +CODEC_ERROR EncodeExtensionTrailer(ENCODER *encoder, BITSTREAM *stream) +{ + return CODEC_ERROR_OKAY; +} + +static int32_t GetMultiplier(QUANT divisor) +{ + switch (divisor) + { + case 1: + return (uint32_t)(1 << 16); + + case 12: + return (1 << 16) / 12; + + case 24: + return (1 << 16) / 24; + + case 32: + return (1 << 16) / 32; + + case 48: + return (1 << 16) / 48; + + case 96: + return (1 << 16) / 96; + + case 144: + return (1 << 16) / 144; + + default: + return (uint32_t)(1 << 16) / divisor; + }; +} + +/*! + @brief Compute the rounding value for quantization + */ +static QUANT QuantizerMidpoint(QUANT correction, QUANT divisor) +{ + int32_t midpoint = 0; + + if (correction == 2) + { + midpoint = divisor >> 1; + + // CFEncode_Premphasis_Original + if (midpoint) { + midpoint--; + } + } + else if (correction > 2 && correction < 9) + { + midpoint = divisor / correction; + } + + return midpoint; +} + +static void GetQuantizationParameters(int32_t midpoint_prequant, QUANT quant[], int32_t* midpoints, int32_t* multipliers) +{ + int i; + for (i = 0; i < 4; i++) + { + midpoints[i] = QuantizerMidpoint(midpoint_prequant, quant[i]); + multipliers[i] = GetMultiplier(quant[i]); + } +} + +/*! + @brief Shift the buffers of horizontal highpass results + + The encoder contains six rows of horizontal lowpass and highpass results + for each channel. This routine shifts the buffers by two rows to make + room for two more rows of horizontal results for each channel. + */ +static void ShiftHorizontalResultBuffers(PIXEL **buffer) +{ + PIXEL *buffer01[2]; + + memcpy( buffer01, buffer + 0, sizeof(PIXEL*) * 2 ); + + memmove( buffer + 0, buffer + 2, sizeof(PIXEL*) * (ROW_BUFFER_COUNT - 2) ); + + memcpy( buffer + 4, buffer01, sizeof(PIXEL*) * 2 ); +} + +typedef struct _recursive_transform_data +{ + PIXEL *input_ptr; + DIMENSION input_width; + DIMENSION input_height; + DIMENSION input_pitch; + + PIXEL *output_ptr[MAX_BAND_COUNT]; + DIMENSION output_width; + DIMENSION output_pitch; + + int32_t prescale; + + int32_t* midpoints; + int32_t* multipliers; + + PIXEL **lowpass_buffer; + PIXEL **highpass_buffer; + +} RECURSIVE_TRANSFORM_DATA; + +#define RECURSIVE 1 + +static void ForwardWaveletTransformRecursive(RECURSIVE_TRANSFORM_DATA *transform_data, int wavelet_stage, uint32_t start_row, uint32_t end_row) +{ + uint32_t input_row_index = start_row; + + PIXEL *input_ptr = transform_data[wavelet_stage].input_ptr; + DIMENSION input_width = transform_data[wavelet_stage].input_width; + DIMENSION input_height = transform_data[wavelet_stage].input_height; + DIMENSION input_pitch = transform_data[wavelet_stage].input_pitch; + + PIXEL **output_ptr = transform_data[wavelet_stage].output_ptr; + DIMENSION output_width = transform_data[wavelet_stage].output_width; + DIMENSION output_pitch = transform_data[wavelet_stage].output_pitch; + + int32_t* midpoints = transform_data[wavelet_stage].midpoints; + int32_t* multipliers = transform_data[wavelet_stage].multipliers; + + PIXEL **lowpass_buffer = transform_data[wavelet_stage].lowpass_buffer; + PIXEL **highpass_buffer = transform_data[wavelet_stage].highpass_buffer; + + int32_t prescale = transform_data[wavelet_stage].prescale; + + int bottom_input_row = ((input_height % 2) == 0) ? input_height - 2 : input_height - 1; + + uint32_t last_middle_row = bottom_input_row - 2; + + end_row = minimum( last_middle_row, end_row); + + // --- TOP ROW + if( input_row_index == 0 ) + { + int row; + + for (row = 0; row < ROW_BUFFER_COUNT; row++) + { + PIXEL *input_row_ptr = (PIXEL *)((uintptr_t)input_ptr + row * input_pitch); + + FilterHorizontalRow(input_row_ptr, lowpass_buffer[row], highpass_buffer[row], input_width, prescale); + } + + // Process the first row as a special case for the boundary condition + FilterVerticalTopRow(lowpass_buffer, highpass_buffer, output_ptr, output_width, output_pitch, midpoints, multipliers, input_row_index ); + input_row_index += 2; + } + + // --- MIDDLE ROWS + for (; input_row_index <= end_row; input_row_index += 2) + { + // Check for errors in the row calculation + assert((input_row_index % 2) == 0); + + FilterVerticalMiddleRow(lowpass_buffer, highpass_buffer, output_ptr, output_width, output_pitch, midpoints, multipliers, input_row_index ); + + if (input_row_index < last_middle_row) + { + int row; + + ShiftHorizontalResultBuffers(lowpass_buffer); + ShiftHorizontalResultBuffers(highpass_buffer); + + // Get two more rows of horizontal lowpass and highpass results + for (row = 4; row < ROW_BUFFER_COUNT; row++) + { + int next_input_row = minimum( input_row_index + row, input_height - 1 ); + + PIXEL *input_row_ptr = (PIXEL *)((uintptr_t)input_ptr + next_input_row * input_pitch); + + FilterHorizontalRow(input_row_ptr, lowpass_buffer[row], highpass_buffer[row], input_width, prescale); + } + } + } + + // --- BOTTOM ROW + if( input_row_index == bottom_input_row ) + { + FilterVerticalBottomRow(lowpass_buffer, highpass_buffer, output_ptr, output_width, output_pitch, midpoints, multipliers, input_row_index ); + } + + if( wavelet_stage < (MAX_WAVELET_COUNT - 1) ) + { + ForwardWaveletTransformRecursive( transform_data, wavelet_stage + 1, 0, 0xFFFF ); + } +} + +static void SetRecursiveTransformData(RECURSIVE_TRANSFORM_DATA* transform_data, + const TRANSFORM *transform, + const COMPONENT_ARRAY *input_image_component, + int32_t midpoints[MAX_BAND_COUNT], int32_t multipliers[MAX_BAND_COUNT], + PIXEL *lowpass_buffer[MAX_WAVELET_COUNT][ROW_BUFFER_COUNT], + PIXEL *highpass_buffer[MAX_WAVELET_COUNT][ROW_BUFFER_COUNT], + int midpoint_prequant, int wavelet_stage ) +{ + int i; + + if( wavelet_stage == 0 ) + { + transform_data->input_width = input_image_component->width; + transform_data->input_height = input_image_component->height; + transform_data->input_pitch = input_image_component->pitch; + transform_data->input_ptr = (PIXEL*)input_image_component->data; + } + else + { + WAVELET *input_wavelet = transform->wavelet[wavelet_stage - 1]; + + transform_data->input_width = input_wavelet->width; + transform_data->input_height = input_wavelet->height; + transform_data->input_pitch = input_wavelet->pitch; + transform_data->input_ptr = WaveletRowAddress(input_wavelet, LL_BAND, 0); + } + + WAVELET *output_wavelet = transform->wavelet[wavelet_stage]; + assert(output_wavelet); + + transform_data->output_width = output_wavelet->width; + transform_data->output_pitch = output_wavelet->pitch; + + for (i = 0; i < MAX_BAND_COUNT; i++) + { + transform_data->output_ptr[i] = output_wavelet->data[i]; + } + + transform_data->lowpass_buffer = lowpass_buffer[wavelet_stage]; + transform_data->highpass_buffer = highpass_buffer[wavelet_stage]; + transform_data->prescale = transform->prescale[wavelet_stage]; + + GetQuantizationParameters(midpoint_prequant, output_wavelet->quant, midpoints, multipliers ); + + transform_data->midpoints = midpoints; + transform_data->multipliers = multipliers; +} + +static void ForwardWaveletTransform(TRANSFORM *transform, const COMPONENT_ARRAY *input_image_component, PIXEL *lowpass_buffer[MAX_WAVELET_COUNT][ROW_BUFFER_COUNT], PIXEL *highpass_buffer[MAX_WAVELET_COUNT][ROW_BUFFER_COUNT], int midpoint_prequant) +{ + RECURSIVE_TRANSFORM_DATA transform_data[MAX_WAVELET_COUNT]; + + int32_t midpoints[MAX_WAVELET_COUNT][MAX_BAND_COUNT]; //!< Midpoint value for each band (applied during quantization) + int32_t multipliers[MAX_WAVELET_COUNT][MAX_BAND_COUNT]; //!< Multiplier value for each band (applied during quantization) + + SetRecursiveTransformData( &transform_data[0], transform, input_image_component, midpoints[0], multipliers[0], lowpass_buffer, highpass_buffer, midpoint_prequant, 0 ); + SetRecursiveTransformData( &transform_data[1], transform, input_image_component, midpoints[1], multipliers[1], lowpass_buffer, highpass_buffer, midpoint_prequant, 1 ); + SetRecursiveTransformData( &transform_data[2], transform, input_image_component, midpoints[2], multipliers[2], lowpass_buffer, highpass_buffer, midpoint_prequant, 2 ); + + ForwardWaveletTransformRecursive( transform_data, 0, 0, 0xFFFF ); +} + +/*! + @brief Encode the portion of a sample that corresponds to a single layer + + Samples can be contain multiple subsamples. Each subsample may correspond to + a different view. For example, an encoded video sample may contain both the + left and right subsamples in a stereo pair. + + Subsamples have been called tracks or channels, but this terminology can be + confused with separate video tracks in a multimedia container or the color + planes that are called channels elsewhere in this codec. + + The subsamples are decoded seperately and composited to form a single frame + that is the output of the complete process of decoding a single video sample. + For this reason, the subsamples are called layers. + + @todo Need to reset the codec state for each layer? +*/ +//CODEC_ERROR EncodeLayer(ENCODER *encoder, void *buffer, size_t pitch, BITSTREAM *stream) +CODEC_ERROR EncodeMultipleChannels(ENCODER *encoder, const UNPACKED_IMAGE *image, BITSTREAM *stream) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + int channel_count; + int channel_index; + + channel_count = encoder->channel_count; + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + if (IsPartEnabled(encoder->enabled_parts, VC5_PART_LAYERS)) + { + // Write the tag value pairs that preceed the encoded wavelet tree + error = EncodeLayerHeader(encoder, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + } +#endif + + + CODEC_STATE *codec = &encoder->codec; + + // Compute the wavelet transform tree for each channel + for (channel_index = 0; channel_index < channel_count; channel_index++) + { + int channel_number; + + ForwardWaveletTransform(&encoder->transform[channel_index], &image->component_array_list[channel_index], encoder->lowpass_buffer, encoder->highpass_buffer, encoder->midpoint_prequant ); + + channel_number = encoder->channel_order_table[channel_index]; + + // Encode the tag value pairs in the header for this channel + error = EncodeChannelHeader(encoder, channel_number, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Encode the lowpass and highpass bands in the wavelet tree for this channel + error = EncodeChannelSubbands(encoder, channel_number, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Encode the tag value pairs in the trailer for this channel + error = EncodeChannelTrailer(encoder, channel_number, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Check that the bitstream is alligned to a segment boundary + assert(IsAlignedSegment(stream)); + + // Update the codec state for the next channel in the bitstream + //codec->channel_number++; + codec->channel_number = (channel_number + 1); + codec->subband_number = 0; + } + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + if (IsPartEnabled(encoder->enabled_parts, VC5_PART_LAYERS)) + { + // Write the tag value pairs that follow the encoded wavelet tree + error = EncodeLayerTrailer(encoder, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + //TODO: Need to align the bitstream between layers? + } +#endif + + return CODEC_ERROR_OKAY; +} + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) +/*! + @brief Write the sample layer header + + The baseline profile only supports a single layer so the layer header + and trailer are not required. +*/ +CODEC_ERROR EncodeLayerHeader(ENCODER *encoder, BITSTREAM *stream) +{ + //TODO: Write the tag-value pair for the layer number + + return CODEC_ERROR_OKAY; +} +#endif +#if VC5_ENABLED_PART(VC5_PART_LAYERS) +/*! + @brief Write the sample layer trailer + + The baseline profile only supports a single layer so the layer header + and trailer are not required. + + If more than one layer is present, the layers must be terminated by a + layer trailer. Otherwise, the decoder will continue to parse tag-value + pairs that belong to the next layer. +*/ +CODEC_ERROR EncodeLayerTrailer(ENCODER *encoder, BITSTREAM *stream) +{ + // The value in the layer trailer tag-value pair is not used + PutTagPairOptional(stream, CODEC_TAG_LAYER_TRAILER, 0); + + return CODEC_ERROR_OKAY; +} +#endif + +/*! + @brief Encode the channel into the bistream + + This routine encodes all of the subbands (lowpass and highpass) in the + wavelet tree for the specified channel into the bitstream. +*/ +CODEC_ERROR EncodeChannelWavelets(ENCODER *encoder, BITSTREAM *stream) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + CODEC_STATE *codec = &encoder->codec; + + int channel_count; + int channel_index; + + // Get the number of channels in the encoder wavelet transform + channel_count = encoder->channel_count; + + // Compute the remaining wavelet transforms for each channel + //for (channel_index = 0; channel_index < channel_count; channel_index++) + for (channel_index = 0; channel_index < channel_count; channel_index++) + { + int channel_number = encoder->channel_order_table[channel_index]; + + // Encode the tag value pairs in the header for this channel + error = EncodeChannelHeader(encoder, channel_number, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Encode the lowpass and highpass bands in the wavelet tree for this channel + error = EncodeChannelSubbands(encoder, channel_number, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Encode the tag value pairs in the trailer for this channel + error = EncodeChannelTrailer(encoder, channel_number, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Check that the bitstream is alligned to a segment boundary + assert(IsAlignedSegment(stream)); + + // Update the codec state for the next channel in the bitstream + //codec->channel_number++; + codec->channel_number = (channel_number + 1); + codec->subband_number = 0; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write the channel header into the bitstream + + The channel header separates channels in the encoded layer. The channel header + is not required before the first encoded channel because the codec state is + initialized for decoding the first channel. + + The first channel is channel number zero. +*/ +CODEC_ERROR EncodeChannelHeader(ENCODER *encoder, + int channel_number, + BITSTREAM *stream) +{ + CODEC_STATE *codec = &encoder->codec; + DIMENSION channel_width = encoder->channel[channel_number].width; + DIMENSION channel_height = encoder->channel[channel_number].height; + int bits_per_component = encoder->channel[channel_number].bits_per_component; + + AlignBitsSegment(stream); + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (IsSectionEnabled(encoder, SECTION_NUMBER_CHANNEL)) + { + // Write the channel section header into the bitstream + BeginChannelSection(encoder, stream); + } +#endif + + // Write the channel number if it does not match the codec state + if (channel_number != codec->channel_number) + { + PutTagPair(stream, CODEC_TAG_ChannelNumber, channel_number); + codec->channel_number = channel_number; + } + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + if (IsPartEnabled(encoder->enabled_parts, VC5_PART_IMAGE_FORMATS)) + { + // The decoder will derive the channel width and height from the image dimensions and format + codec->channel_width = channel_width; + codec->channel_height = channel_height; + } + else +#endif + { + // Write the component array width if it does not match the codec state + if (channel_width != codec->channel_width) + { + PutTagPair(stream, CODEC_TAG_ChannelWidth, channel_width); + codec->channel_width = channel_width; + } + + // Write the component array height if it does not match the codec state + if (channel_height != codec->channel_height) + { + PutTagPair(stream, CODEC_TAG_ChannelHeight, channel_height); + codec->channel_height = channel_height; + } + } + + // Write the component array precision if it does not match the codec state + if (bits_per_component != codec->bits_per_component) + { + PutTagPair(stream, CODEC_TAG_BitsPerComponent, bits_per_component); + codec->bits_per_component = bits_per_component; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write the encoded subbands for this channel into the bitstream + + This routine writes the encoded subbands in the wavelet tree for this channel + into the bitstream, including both the lowpass band and all of the highpass + bands in each wavelet in this channel. +*/ +CODEC_ERROR EncodeChannelSubbands(ENCODER *encoder, int channel_number, BITSTREAM *stream) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + //CODEC_STATE *codec = &encoder->codec; + + int wavelet_count = encoder->wavelet_count; + int last_wavelet_index = wavelet_count - 1; + int wavelet_index; + + int subband = 0; + + // Start with the lowpass band in the wavelet at the highest level + WAVELET *wavelet = encoder->transform[channel_number].wavelet[last_wavelet_index]; + + // Check that the bitstream is aligned on a segment boundary + assert(IsAlignedSegment(stream)); + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (IsSectionEnabled(encoder, SECTION_NUMBER_WAVELET)) + { + // Write the wavelet section header into the bitstream + BeginWaveletSection(encoder, stream); + } +#endif + + // Encode the lowpass subband in this channel + error = EncodeLowpassBand(encoder, wavelet, channel_number, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Advance to the first highpass subband + subband++; + + // Encode the highpass bands in order of subband number + for (wavelet_index = last_wavelet_index; wavelet_index >= 0; wavelet_index--) + { + //int wavelet_type = WAVELET_TYPE_SPATIAL; + //int wavelet_level = wavelet_index + 1; + int band_index; + + //int lowpass_scale = 0; + //int lowpass_divisor = 0; + + wavelet = encoder->transform[channel_number].wavelet[wavelet_index]; + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (IsSectionEnabled(encoder, SECTION_NUMBER_WAVELET)) + { + // Was the wavelet section header already written into the bitstream? + if (wavelet_index < last_wavelet_index) + { + // Write the wavelet section header into the bitstream + BeginWaveletSection(encoder, stream); + } + } +#endif + // Encode the highpass bands in this wavelet + for (band_index = 1; band_index < wavelet->band_count; band_index++) + { + error = EncodeHighpassBand(encoder, wavelet, band_index, subband, stream); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Advance to the next subband + subband++; + } + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (IsSectionEnabled(encoder, SECTION_NUMBER_WAVELET)) + { + // Make sure that the bitstream is aligned to a segment boundary + AlignBitsSegment(stream); + + // Update the section header with the actual size of the wavelet section + EndSection(stream); + } +#endif + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write the channel trailer into the bitstream + + A channel trailer is not required as the channel header functions as a marker + between channels in the bitstream. + + It may be necessary to update the channel size in a sample size segment written + into the channel header if the channel header includes a sample size segment in + the future. +*/ +CODEC_ERROR EncodeChannelTrailer(ENCODER *encoder, int channel, BITSTREAM *stream) +{ +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (IsSectionEnabled(encoder, SECTION_NUMBER_CHANNEL)) + { + // Make sure that the bitstream is aligned to a segment boundary + AlignBitsSegment(stream); + + // Update the section header with the actual size of the channel section + EndSection(stream); + } +#endif + return CODEC_ERROR_OKAY; +} + +/*! + @brief Allocate intermediate buffers for the horizontal transform results + + @todo Need to return an error code if allocation fails +*/ +CODEC_ERROR AllocateEncoderHorizontalBuffers(ENCODER *encoder) +{ + gpr_allocator *allocator = encoder->allocator; + int channel_index; + int wavelet_index; + int channel_count = encoder->channel_count; + + int buffer_width = 0; + + for (channel_index = 0; channel_index < channel_count; channel_index++) + { + buffer_width = maximum(buffer_width, encoder->channel[channel_index].width ); + } + + buffer_width = ((buffer_width % 2) == 0) ? buffer_width / 2 : (buffer_width + 1) / 2; + + for (wavelet_index = 0; wavelet_index < MAX_WAVELET_COUNT; wavelet_index++) + { + int row; + + int channel_width = encoder->transform[0].wavelet[wavelet_index]->width; + + for (row = 0; row < ROW_BUFFER_COUNT; row++) + { + PIXEL *lowpass_buffer = allocator->Alloc(channel_width * sizeof(PIXEL) * 2); + PIXEL *highpass_buffer = lowpass_buffer + channel_width; + + assert(lowpass_buffer != NULL); + if (! (lowpass_buffer != NULL)) { + return CODEC_ERROR_OUTOFMEMORY; + } + + encoder->lowpass_buffer[wavelet_index][row] = lowpass_buffer; + encoder->highpass_buffer[wavelet_index][row] = highpass_buffer; + } + } + + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Deallocate the intermediate buffers for the horizontal transform results + + It is possible to avoid reallocating the buffers for the horizontal transform + results if the buffers were not deallocated between encoded frames. In this case, + it would be necessary to call this routine inside @ref ReleaseEncoder and it would + also be necessary to modify @ref AllocateEncoderHorizontalBuffers to not allocate + the buffers if they are already allocated. +*/ +CODEC_ERROR DeallocateEncoderHorizontalBuffers(ENCODER *encoder) +{ + gpr_allocator *allocator = encoder->allocator; + + int wavelet_index; + + for (wavelet_index = 0; wavelet_index < MAX_WAVELET_COUNT; wavelet_index++) + { + int row; + + for (row = 0; row < ROW_BUFFER_COUNT; row++) + { + allocator->Free(encoder->lowpass_buffer[wavelet_index][row]); + } + } + + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Allocate buffers used for computing the forward wavelet transform +*/ +CODEC_ERROR AllocateHorizontalBuffers(gpr_allocator *allocator, + PIXEL *lowpass_buffer[], + PIXEL *highpass_buffer[], + int buffer_width) +{ + const size_t row_buffer_size = buffer_width * sizeof(PIXEL); + + int row; + + for (row = 0; row < ROW_BUFFER_COUNT; row++) + { + lowpass_buffer[row] = allocator->Alloc(row_buffer_size); + highpass_buffer[row] = allocator->Alloc(row_buffer_size); + + // Check that the memory allocation was successful + assert(lowpass_buffer[row] != NULL); + if (! (lowpass_buffer[row] != NULL)) { + return CODEC_ERROR_OUTOFMEMORY; + } + assert(highpass_buffer[row] != NULL); + if (! (highpass_buffer[row] != NULL)) { + return CODEC_ERROR_OUTOFMEMORY; + } + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Deallocate buffers used for computing the forward wavelet transform +*/ +CODEC_ERROR DeallocateHorizontalBuffers(gpr_allocator *allocator, + PIXEL *lowpass_buffer[], + PIXEL *highpass_buffer[]) +{ + int row; + + for (row = 0; row < ROW_BUFFER_COUNT; row++) + { + allocator->Free(lowpass_buffer[row]); + allocator->Free(highpass_buffer[row]); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Allocate all of the wavelets used during encoding + + This routine allocates all of the wavelets in the wavelet tree that + may be used during encoding. + + This routine is used to preallocate the wavelets before encoding begins. + If the wavelet bands are allocated on demand if not preallocated. + + By default, the wavelet bands are encoded into the bitstream with the bands + from the wavelet at the highest level (smallest wavelet) first so that the + bands can be processed by the encoder in the order as the sample is decoded. + + @todo Do not allocate wavelets for resolutions that are larger then the + decoded resolution. At lower resolutions, the depth of the wavelet tree + can be reduced and the highpass bands in the unused wavelets to not have + to be decoded. + + @todo Should it be an error if the wavelets are not preallocated? +*/ +CODEC_ERROR AllocEncoderTransforms(ENCODER *encoder) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Use the default allocator for the encoder + gpr_allocator *allocator = encoder->allocator; + int channel_index; + int wavelet_index; + + assert(encoder != NULL); + if (! (encoder != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + // Check that the encoded dimensions are valid + //assert((encoder->encoded.width % (1 << encoder->wavelet_count)) == 0); + + for (channel_index = 0; channel_index < encoder->channel_count; channel_index++) + { + // The wavelet at level zero has the same dimensions as the encoded frame + DIMENSION wavelet_width = 0; + DIMENSION wavelet_height = 0; + error = GetChannelDimensions(encoder, channel_index, &wavelet_width, &wavelet_height); + assert(wavelet_width > 0 && wavelet_height > 0); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + for (wavelet_index = 0; wavelet_index < encoder->wavelet_count; wavelet_index++) + { + WAVELET *wavelet = NULL; + + // Pad the wavelet width if not divisible by two + if ((wavelet_width % 2) != 0) { + wavelet_width++; + } + + // Pad the wavelet height if not divisible by two + if ((wavelet_height % 2) != 0) { + wavelet_height++; + } + + // Reduce the dimensions of the next wavelet by half + wavelet_width /= 2; + wavelet_height /= 2; + + // Dimensions of the current wavelet must be divisible by two + //assert((wavelet_width % 2) == 0 && (wavelet_height % 2) == 0); + + // The wavelet width must be divisible by two + //assert((wavelet_width % 2) == 0); + + // Allocate the wavelet + wavelet = CreateWavelet(allocator, wavelet_width, wavelet_height); + if (wavelet == NULL) { + return CODEC_ERROR_OUTOFMEMORY; + } + + // Add the wavelet to the transform + encoder->transform[channel_index].wavelet[wavelet_index] = wavelet; + } + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Allocate all of the buffers required for encoding + + This routine allocates buffers required for encoding, not including + the wavelet images in the wavelet tree which are allocated by + @ref AllocEncoderTransforms + + This routine is used to preallocate buffers before encoding begins. + If the buffers are allocated on demand if not preallocated. + + The encoding parameters, including the encoded frame dimensions, + resolution of the decoded frame, and the decoded pixel format, are + taken into account when the buffers are allocated. For example, + buffer space that is only used when encoding to full resolution will + not be allocated if the frame is decoded to a smaller size. + + Note that it is not an error to preallocate more buffer space than + what is strictly required for encoding. For example, it is okay to + allocate buffer space required for full frame encoding even if the + encoded sample will be decoded at lower resolution. In many applications, + it is simpler to preallocate the maximum buffer space that may be needed. + + Currently, the reference encoder allocates scratch buffers as required + by each routine that needs scratch space and the scratch buffers are + deallocated at the end each routine that allocates scratch space. + A custom memory allocator can make this scheme efficient. See comments + in the documentation for the memory allocator module. + + @todo Should it be an error if the buffers are not preallocated? +*/ +CODEC_ERROR AllocEncoderBuffers(ENCODER *encoder) +{ + (void)encoder; + return CODEC_ERROR_UNIMPLEMENTED; +} + +/*! + @brief Set the quantization parameters in the encoder + + This routine computes the parameters in the quantizer used by + the encoder based based on the quality setting and the desired + bitrate. The quantization parameters are adjsuted to compensate + for the precision of the input pixels. + + Note that the baseline profile does not support quantization to + achieve a desired bitrate. + +*/ +CODEC_ERROR SetEncoderQuantization(ENCODER *encoder, + const ENCODER_PARAMETERS *parameters) +{ + int channel_count = encoder->channel_count; + int channel_number; + + const int quant_table_length = sizeof(parameters->quant_table)/sizeof(parameters->quant_table[0]); + + // Set the midpoint prequant parameter + encoder->midpoint_prequant = 2; + + // Set the quantization table in each channel + for (channel_number = 0; channel_number < channel_count; channel_number++) + { + SetTransformQuantTable(encoder, channel_number, parameters->quant_table, quant_table_length); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Copy the quantization table into the wavelet bands +*/ +CODEC_ERROR SetTransformQuantTable(ENCODER *encoder, int channel, const QUANT table[], int table_length) +{ + int wavelet_count = encoder->wavelet_count; + int wavelet_index; + int subband; + + // All lowpass bands use the quantization for subband zero + for (wavelet_index = 0; wavelet_index < wavelet_count; wavelet_index++) + { + WAVELET *wavelet = encoder->transform[channel].wavelet[wavelet_index]; + wavelet->quant[0] = table[0]; + } + + // Store the quantization values for the highpass bands in each wavelet + for (subband = 1; subband < table_length; subband++) + { + int wavelet_index = SubbandWaveletIndex(subband); + int band_index = SubbandBandIndex(subband); + WAVELET *wavelet; + + assert(0 <= wavelet_index && wavelet_index < wavelet_count); + assert(0 <= band_index && band_index <= MAX_BAND_COUNT); + + // Store the quantization value for this subband + wavelet = encoder->transform[channel].wavelet[wavelet_index]; + wavelet->quant[band_index] = table[subband]; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Return the encoded dimensions for the specified channel + + The encoded dimensions for each channel may differ due to color + difference component sampling. +*/ +CODEC_ERROR GetChannelDimensions(ENCODER *encoder, + int channel_number, + DIMENSION *channel_width_out, + DIMENSION *channel_height_out) +{ + DIMENSION channel_width = 0; + DIMENSION channel_height = 0; + + assert(encoder != NULL && channel_width_out != NULL && channel_height_out != NULL); + if (! (encoder != NULL && channel_width_out != NULL && channel_height_out != NULL)) { + return CODEC_ERROR_NULLPTR; + } + + assert(0 <= channel_number && channel_number < encoder->channel_count); + if (! (0 <= channel_number && channel_number < encoder->channel_count)) { + return CODEC_ERROR_UNEXPECTED; + } + + // Clear the output dimensions in case this routine terminates early + *channel_width_out = 0; + *channel_height_out = 0; + + channel_width = encoder->channel[channel_number].width; + channel_height = encoder->channel[channel_number].height; + + *channel_width_out = channel_width; + *channel_height_out = channel_height; + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Adjust the height of encoded layer + + Interleaved frames are encoded as separate layers with half the height. +*/ +DIMENSION EncodedLayerHeight(ENCODER *encoder, DIMENSION height) +{ +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + assert(encoder != NULL); + if (encoder->progressive == 0) { + height /= 2; + } +#endif + + return height; +} + +/*! + @brief Compute the dimensions of the image as reported by the ImageWidth and ImageHeight parameters + + The image width is the maximum width of all component arrays and the image height is the maximum height + of all component arrays. +*/ +CODEC_ERROR GetMaximumChannelDimensions(const UNPACKED_IMAGE *image, DIMENSION *width_out, DIMENSION *height_out) +{ + DIMENSION width = 0; + DIMENSION height = 0; + int channel_number; + + if (image == NULL) { + return CODEC_ERROR_UNEXPECTED; + } + + for (channel_number = 0; channel_number < image->component_count; channel_number++) + { + if (width < image->component_array_list[channel_number].width) { + width = image->component_array_list[channel_number].width; + } + + if (height < image->component_array_list[channel_number].height) { + height = image->component_array_list[channel_number].height; + } + } + + if (width_out != NULL) { + *width_out = width; + } + + if (height_out != NULL) { + *height_out = height; + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Set the bit for the specified subband in the decoded band mask + + The decoded subband mask is used to track which subbands have been + decoded in teh current channel. It is reset at the start of each + channel. + + The decoded subband mask is used when decoding a sample at less + than full resolution. The mask indicates when enough subbands + have been decoded for a channel and that remaining portion of the + encoded sample for the current channel may be skipped. +*/ +CODEC_ERROR SetEncodedBandMask(CODEC_STATE *codec, int subband) +{ + if (0 <= subband && subband < MAX_SUBBAND_COUNT) { + codec->decoded_subband_mask |= (1 << subband); + } + return CODEC_ERROR_OKAY; +} + + +/*! + @brief Encoded the lowpass band from the bitstream + + The wavelet at the highest level is passes as an argument. + This routine decodes lowpass band in the bitstream into the + lowpass band of the wavelet. +*/ +CODEC_ERROR EncodeLowpassBand(ENCODER *encoder, WAVELET *wavelet, int channel_number, BITSTREAM *stream) +{ + CODEC_STATE *codec = &encoder->codec; + //FILE *logfile = encoder->logfile; + //int subband = 0; + //int level = encoder->wavelet_count; + int width = wavelet->width; + int height = wavelet->height; + uint8_t *lowpass_row_ptr; + int lowpass_pitch; + int row; + + PRECISION lowpass_precision = encoder->channel[channel_number].lowpass_precision; + + lowpass_row_ptr = (uint8_t *)wavelet->data[LL_BAND]; + lowpass_pitch = wavelet->pitch; + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (IsSectionEnabled(encoder, SECTION_NUMBER_SUBBAND)) + { + // Make sure that the bitstream is aligned to a segment boundary + AlignBitsSegment(stream); + + // Write the channel section header into the bitstream + BeginSubbandSection(encoder, stream); + } +#endif + + // Write the tag-value pairs for the lowpass band to the bitstream + PutVideoLowpassHeader(encoder, channel_number, stream); + + // Check that the bitstream is tag aligned before writing the pixels + assert(IsAlignedSegment(stream)); + + for (row = 0; row < height; row++) + { + uint16_t *lowpass = (uint16_t *)lowpass_row_ptr; + int column; + + for (column = 0; column < width; column++) + { + BITWORD coefficient = lowpass[column]; + //assert(0 <= lowpass[column] && lowpass[column] <= COEFFICIENT_MAX); + assert(lowpass[column] <= COEFFICIENT_MAX); + assert(coefficient <= COEFFICIENT_MAX); + PutBits(stream, coefficient, lowpass_precision); + } + + lowpass_row_ptr += lowpass_pitch; + } + + // Align the bitstream to a segment boundary + AlignBitsSegment(stream); + + PutVideoLowpassTrailer(stream); + + // Update the subband number in the codec state + codec->subband_number++; + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (IsSectionEnabled(encoder, SECTION_NUMBER_SUBBAND)) + { + // Make sure that the bitstream is aligned to a segment boundary + AlignBitsSegment(stream); + + // Update the section header with the actual size of the subband section + EndSection(stream); + } +#endif + + return CODEC_ERROR_OKAY; +} + +CODEC_ERROR PutVideoSubbandHeader(ENCODER *encoder, int subband_number, QUANT quantization, BITSTREAM *stream) +{ + CODEC_STATE *codec = &encoder->codec; + + if (subband_number != codec->subband_number) { + PutTagPair(stream, CODEC_TAG_SubbandNumber, subband_number); + codec->subband_number = subband_number; + } + + if (quantization != codec->band.quantization) { + PutTagPair(stream, CODEC_TAG_Quantization, quantization); + codec->band.quantization = quantization; + } + + // Write the chunk header for the codeblock + PushSampleSize(stream, CODEC_TAG_LargeCodeblock); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Encode the highpass band into the bitstream + + The specified wavelet band is decoded from the bitstream + using the codebook and encoding method specified in the + bitstream. +*/ +CODEC_ERROR EncodeHighpassBand(ENCODER *encoder, WAVELET *wavelet, int band, int subband, BITSTREAM *stream) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + CODEC_STATE *codec = &encoder->codec; + + DIMENSION band_width = wavelet->width; + DIMENSION band_height = wavelet->height; + + void *band_data = wavelet->data[band]; + DIMENSION band_pitch = wavelet->pitch; + + QUANT quantization = wavelet->quant[band]; + //uint16_t scale = wavelet->scale[band]; + + //int divisor = 0; + //int peaks_coding = 0; + + ENCODER_CODESET *codeset = encoder->codeset; + + //int encoding_method = BAND_ENCODING_RUNLENGTHS; + + // Check that the band header starts on a tag boundary + assert(IsAlignedTag(stream)); + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (IsSectionEnabled(encoder, SECTION_NUMBER_SUBBAND)) + { + // Make sure that the bitstream is aligned to a segment boundary + AlignBitsSegment(stream); + + // Write the channel section header into the bitstream + BeginSubbandSection(encoder, stream); + } +#endif + + // Output the tag-value pairs for this subband + PutVideoSubbandHeader(encoder, subband, quantization, stream); + + // Encode the highpass coefficients for this subband into the bitstream + error = EncodeHighpassBandRowRuns(stream, codeset, band_data, band_width, band_height, band_pitch); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Align the bitstream to a segment boundary + AlignBitsSegment(stream); + + // Output the band trailer + PutVideoSubbandTrailer(encoder, stream); + + // Update the subband number in the codec state + codec->subband_number++; + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + if (IsSectionEnabled(encoder, SECTION_NUMBER_SUBBAND)) + { + // Make sure that the bitstream is aligned to a segment boundary + AlignBitsSegment(stream); + + // Update the section header with the actual size of the subband section + EndSection(stream); + } +#endif + + return CODEC_ERROR_OKAY; +} + +STATIC_INLINE void write_bits(uint8_t** buffer, uint32_t bits) +{ + uint32_t word = Swap32(bits); + *( (uint32_t*)(*buffer) ) = word; +} + +STATIC_INLINE VLE PutZeroBits(uint8_t** buffer, VLE stream_bits, uint_fast8_t size ) +{ + BITCOUNT unused_bit_count = bit_word_count - stream_bits.size; + + if ( size > unused_bit_count ) + { + if (stream_bits.size < bit_word_count) + { + size -= unused_bit_count; + } + + write_bits(buffer, stream_bits.bits); + *buffer += 4; + + stream_bits.size = size; + stream_bits.bits = 0; + } + else + { + stream_bits.size += size; + } + + return stream_bits; +} + +STATIC_INLINE VLE PutBitsCore(uint8_t** buffer, VLE stream_bits, uint32_t bits, uint_fast8_t size ) +{ + BITCOUNT unused_bit_count = bit_word_count - stream_bits.size; + + if ( size > unused_bit_count) + { + if (stream_bits.size < bit_word_count) + { + stream_bits.bits |= (bits >> (size - unused_bit_count)); + size -= unused_bit_count; + } + + write_bits(buffer, stream_bits.bits); + *buffer += 4; + + stream_bits.size = size; + stream_bits.bits = bits << (bit_word_count - size); + } + else + { + stream_bits.bits |= (bits << (unused_bit_count - size)); + stream_bits.size += size; + } + + return stream_bits; +} + +STATIC_INLINE VLE PutBitsCoreWithSign(uint8_t** buffer, VLE stream_bits, uint32_t bits, uint_fast8_t size, bool positive ) +{ + stream_bits = PutBitsCore( buffer, stream_bits, bits, size ); + + BITCOUNT unused_bit_count = bit_word_count - stream_bits.size; + + if ( unused_bit_count == 0 ) + { + write_bits(buffer, stream_bits.bits); + *buffer += 4; + + stream_bits.size = 1; + + if( positive == false ) + stream_bits.bits = 1 << (bit_word_count - 1); + else + stream_bits.bits = 0; + } + else + { + stream_bits.size += 1; + + if( positive == false ) + stream_bits.bits |= (1 << (unused_bit_count - 1)); + } + + return stream_bits; +} + +/*! + @brief Encode the highpass band from the bitstream + + This routine does not encode runs of zeros across row boundaries. +*/ +CODEC_ERROR EncodeHighpassBandRowRuns(BITSTREAM *stream, ENCODER_CODESET *codeset, PIXEL *data, + DIMENSION width, DIMENSION height, DIMENSION pitch) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + int row_padding; + int row = 0; + //int column = 0; + //size_t index = 0; + + // The encoder uses the codebooks for magnitudes and runs of zeros + const MAGS_TABLE *mags_table = codeset->mags_table; + const RUNS_TABLE *runs_table = codeset->runs_table; + int runs_table_length = runs_table->length; + RLC *rlc = (RLC *)((uint8_t *)runs_table + sizeof(RUNS_TABLE)); + + // The band is terminated by the band end codeword in the codebook + const CODEBOOK *codebook = codeset->codebook; + + PIXEL *rowptr = data; + int count = 0; + + // Convert the pitch to units of pixels + assert((pitch % sizeof(PIXEL)) == 0); + pitch /= sizeof(PIXEL); + + // Check that the band dimensions are reasonable + assert(width <= pitch); + + // Compute the number of values of padding at the end of each row + row_padding = pitch - width; + + VLE *mags_table_entry = (VLE *)((uint8_t *)mags_table + sizeof(MAGS_TABLE)); + + VLE stream_bits; + + stream_bits.bits = stream->buffer; + stream_bits.size = stream->count; + + struct _stream *bit_stream = stream->stream; + + int mags_table_length_minus_1 = mags_table->length - 1; + + uint8_t* stream_buffer = (uint8_t *)bit_stream->location.memory.buffer + bit_stream->byte_count; + uint8_t* stream_buffer_orig = stream_buffer; + + for (row = 0; row < height; row++) + { + int index = 0; // Start at the beginning of the row + + // Search the row for runs of zeros and nonzero values + while (1) + { + // Loop invariant + assert(0 <= index && index < width); + + { + PIXEL* start = rowptr + index; + PIXEL* end = rowptr + width; + + for (; *(start) == 0 && start != end; start++) + { + + } + + int x = start - (rowptr + index); + + index += x; + count += x; + } + + // Need to output a value? + if (index < width) + { + while (count > 0) + { + if( count < 12 ) + { + stream_bits = PutZeroBits(&stream_buffer, stream_bits, count ); + break; + } + else + { + int count_index = minimum(count, runs_table_length - 1); + assert(count_index < runs_table->length); + + RLC rlc_val = rlc[count_index]; + + stream_bits = PutBitsCore(&stream_buffer, stream_bits, rlc_val.bits, rlc_val.size ); + + // Reduce the length of the run by the amount output + count -= rlc_val.count; + } + } + + count = 0; + + // The value zero is run length coded and handled by another routine + { + PIXEL value = rowptr[index++]; + assert(value != 0); + + PIXEL abs_value = minimum( abs(value), mags_table_length_minus_1 ); + + stream_bits = PutBitsCoreWithSign(&stream_buffer, stream_bits, mags_table_entry[abs_value].bits, mags_table_entry[abs_value].size, value > 0 ); + } + } + + // Add the end of row padding to the encoded length + if (index == width) + { + count += row_padding; + break; + } + } + + // Should have processed the entire row + assert(index == width); + + // Advance to the next row + rowptr += pitch; + } + + stream->count = stream_bits.size; + stream->buffer = stream_bits.bits; + bit_stream->byte_count += (stream_buffer - stream_buffer_orig); + + // // Need to output a pending run of zeros? + if (count > 0) + { + error = PutZeros(stream, runs_table, count); + if (error != CODEC_ERROR_OKAY) { + return error; + } + } + + // Insert the special codeword that marks the end of the highpass band + error = PutSpecial(stream, codebook, SPECIAL_MARKER_BAND_END); + + return error; +} + +CODEC_ERROR PutVideoSubbandTrailer(ENCODER *encoder, BITSTREAM *stream) +{ + // Set the size of the large chunk for the highpass band codeblock + PopSampleSize(stream); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Read the segment at the specified offset in the bitstream + + This routine is used to read a segment that was previously written at a previous + location in the encoded sample. This allows the encoder to update, rather than + overwrite, a segment that has already been written. Typically, this is done to + insert the size or offset to a portion of the sample (syntax element) into a + segment that acts as an index to the syntax element. + */ +CODEC_ERROR GetSampleOffsetSegment(BITSTREAM *bitstream, uint32_t offset, TAGVALUE *segment) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + uint32_t buffer; + + error = GetBlock(bitstream->stream, &buffer, sizeof(buffer), offset); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Translate the segment to native byte order + segment->longword = Swap32(buffer); + + // Cannot return a segment if the offset stack is empty + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write the lowpass band header into the bitstream + + Each channel is encoded separately, so the lowpass band (subband zero) + is the lowpass band in the wavelet at the highest level for each channel. + + The last element in the lowpass band header is a segment that contains the + size of this subband. The actual size is updated when the lowpass trailer + is written (see @ref PutVideoLowpassTrailer). + + The lowpass start code is used to uniquely identify the start of the lowpass + band header and is used by the decode to navigate to the next channel in the + bitstream. + + @todo Consider writing a composite lowpass band for all channels with + interleaved rows to facilitate access to the thumbnail image in the + encoded sample. + */ +CODEC_ERROR PutVideoLowpassHeader(ENCODER *encoder, int channel_number, BITSTREAM *stream) +{ + CODEC_STATE *codec = &encoder->codec; + PRECISION lowpass_precision = encoder->channel[channel_number].lowpass_precision; + + // Output the subband number + if (codec->subband_number != 0) + { + PutTagPair(stream, CODEC_TAG_SubbandNumber, 0); + codec->subband_number = 0; + } + + // Output the lowpass precision + //if (encoder->lowpass.precision != codec->lowpass.precision) + if (lowpass_precision != codec->lowpass_precision) + { + PutTagPair(stream, CODEC_TAG_LowpassPrecision, lowpass_precision); + codec->lowpass_precision = lowpass_precision; + } + + // Write the chunk header for the codeblock + PushSampleSize(stream, CODEC_TAG_LargeCodeblock); + + return CODEC_ERROR_OKAY; +} + diff --git a/source/lib/vc5_encoder/encoder.h b/source/lib/vc5_encoder/encoder.h new file mode 100755 index 0000000..1048628 --- /dev/null +++ b/source/lib/vc5_encoder/encoder.h @@ -0,0 +1,333 @@ +/*! @file encoder.h + * + * @brief Declaration of the data structures and constants used for core encoding. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ENCODER_H +#define ENCODER_H + +/*! + @brief Data structure for the pixel or picture aspect ratio + + @todo Should the members of the aspect ratio data structure be unsigned? +*/ +typedef struct _aspect_ratio +{ + int16_t x; //!< Numerator of the aspect ratio + int16_t y; //!< Denominator of the aspect ratio + +} ASPECT_RATIO; + +/*! + @brief Data structure for the buffers and information used by the encoder + + The encoder data structure contains information that will be + used by the encoder for decoding every sample in the sequence. + Information that varies during decoding, such as the current + subband index or the dimensions of the bands in the wavelet that + is being decoded, is stored in the codec state. + + The encoded dimensions are the width and height of the array of pixels + for each encoded channel (image plane), including padding added to + satisfy the requirements of the wavelet transforms. In the case + of 4:2:2 sampling, the encoded width and height are for the luma channel. + + The display dimensions are the width and height of the display aperture, + the displayable portion of the decoded image with padding removed. + + The display dimensions can include a row and column offset to trim + top rows and left columns from the decoded image prior to display. + + The decoded dimensions equal the encoded dimensions at full resolution + and are reduced by a power of two if decoded to a lower resolution. + The decoded dimensions are derived from the encoded dimensions and the + decoded resolution. + + The decoded dimensions are used to allocate the wavelet tree for the + lowpass and highpass coefficients decoded from the bitstream. It is + not necessary to allocate wavelets for larger resolutions than the + decoded resolution. + + For Bayer encoded images, the encoded dimensions are half the width + and height of the input dimensions (after windowing). Typically, + media containers report the display dimensions as twice the encoded + dimensions since a demosaic algorithm must be applied to produce a + displayable image that looks right to most people. + + @todo Consider changing the transform data structure to use a + vector of wavelets rather than a vector of wavelet pointers. +*/ +typedef struct _encoder +{ + // CODEC codec; //!< Common fields for both the encoder and decoder + + FILE *logfile; //!< File for writing debugging information + CODEC_ERROR error; //!< Error code from the most recent codec operation + gpr_allocator *allocator; //!< Memory allocator used to allocate all dyynamic data + CODEC_STATE codec; //!< Information gathered while decoding the current sample + VERSION version; //!< Codec version (major, minor, revision, build) + + //! Parts of the VC-5 standard that are supported at runtime by the codec implementation + ENABLED_PARTS enabled_parts; + + uint64_t frame_number; //!< Every sample in a clip has a unique frame number + + //! Number of color channels in the input and encoded images + uint_fast8_t channel_count; + + //! Number of wavelet transforms in each channel + uint_fast8_t wavelet_count; + + //! Internal precision used by this encoder + PRECISION internal_precision; + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + IMAGE_FORMAT image_format; //!< Type of the image represented by the bitstream + DIMENSION image_width; //!< Number of samples per row in the image represented by the bitstream + DIMENSION image_height; //!< Number of rows of samples in the image represented by the bitstream + DIMENSION pattern_width; //!< Number of samples per row in each pattern element + DIMENSION pattern_height; //!< Number of rows of samples in each pattern element + DIMENSION components_per_sample; //!< Number of components per sample in the image + DIMENSION max_bits_per_component; //!< Upper bound on the number of significant bits per component value +#else + DIMENSION image_width; //!< Upper bound on the width of each channel + DIMENSION image_height; //!< Upper bound on the height of each channel +#endif + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + //! Progressive frame flag + BOOLEAN progressive; + + // Interlaced frame with the top field encoded first + BOOLEAN top_field_first; + + // The encoded frame is upside down (not used) + BOOLEAN frame_inverted; +#endif + + struct _channel + { + DIMENSION width; //!< Width of the next channel in the bitstream + DIMENSION height; //!< Height of the next channel in the bitstream + + //! Precision of the component array for the next channel in the bitstream + PRECISION bits_per_component; + + //! Number of bits per lowpass coefficient + PRECISION lowpass_precision; + + } channel[MAX_CHANNEL_COUNT]; //!< Information about each channel + + //! Dimensions and format of the image that was input to the encoder + struct _input + { + DIMENSION width; //!< Width of the image input to the encoder + DIMENSION height; //!< Height of the image input to the encoder + PIXEL_FORMAT format; //!< Pixel format of the image input to the encode + + } input; //!< Information about the image input to the encoder + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + uint_least8_t layer_count; //!< Number of subsamples in each sample +#endif + + //! Wavelet tree for each channel + TRANSFORM transform[MAX_CHANNEL_COUNT]; + + //! Codebook to use for encoding + ENCODER_CODESET *codeset; + + //! Scratch buffer for unpacking the input image + PIXEL *unpacked_buffer[MAX_CHANNEL_COUNT]; + + //! Parameter that controls the amount of rounding before quantization + int midpoint_prequant; + + //! Table for the order in which channels are encoded into the bitstream + CHANNEL channel_order_table[MAX_CHANNEL_COUNT]; + + //! Number of entries in the channel order table (may be less than the channel count) + int channel_order_count; + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + uint8_t image_sequence_identifier[16]; //!< UUID used for the unique image identifier + uint32_t image_sequence_number; //!< Number of the image in the encoded sequence +#endif + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + COMPONENT_TRANSFORM *component_transform; + COMPONENT_PERMUTATION *component_permutation; +#endif + + //! Six rows of horizontal lowpass results for each channel + PIXEL *lowpass_buffer[MAX_WAVELET_COUNT][ROW_BUFFER_COUNT]; + + //! Six rows of horizontal highpass results for each channel + PIXEL *highpass_buffer[MAX_WAVELET_COUNT][ROW_BUFFER_COUNT]; + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + ENABLED_SECTIONS enabled_sections; +#endif + +} ENCODER; + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR InitEncoder(ENCODER *encoder, const gpr_allocator *allocator, const VERSION *version); + + //TAGWORD PackedEncoderVersion(ENCODER *encoder); + + CODEC_ERROR PrepareEncoder(ENCODER *encoder, + const UNPACKED_IMAGE *image, + const ENCODER_PARAMETERS *parameters); + + CODEC_ERROR PrepareEncoderState(ENCODER *encoder, + const UNPACKED_IMAGE *image, + const ENCODER_PARAMETERS *parameters); + + CODEC_ERROR SetInputChannelFormats(ENCODER *encoder, ENCODER_PARAMETERS *parameters); + + CODEC_ERROR ReleaseEncoder(ENCODER *encoder); + + CODEC_ERROR AllocEncoderTransforms(ENCODER *encoder); + + CODEC_ERROR AllocEncoderBuffers(ENCODER *encoder); + + //CODEC_ERROR EncodeStream(IMAGE *image, STREAM *stream, PARAMETERS *parameters); + CODEC_ERROR EncodeImage(IMAGE *image, STREAM *stream, RGB_IMAGE *rgb_image, ENCODER_PARAMETERS *parameters); + + //CODEC_ERROR EncodeSingleImage(ENCODER *encoder, IMAGE *image, BITSTREAM *stream); + CODEC_ERROR EncodingProcess(ENCODER *encoder, + const UNPACKED_IMAGE *image, + BITSTREAM *stream, + const ENCODER_PARAMETERS *parameters); + + CODEC_ERROR EncodeSingleImage(ENCODER *encoder, const UNPACKED_IMAGE *image, BITSTREAM *stream); + + CODEC_ERROR EncodeSingleChannel(ENCODER *encoder, void *buffer, size_t pitch, BITSTREAM *stream); + + //CODEC_ERROR EncodeMultipleImages(ENCODER *encoder, IMAGE *image_array[], int frame_count, BITSTREAM *stream); + + CODEC_ERROR PrepareEncoderTransforms(ENCODER *encoder); + + //CODEC_ERROR ImageUnpackingProcess(ENCODER *encoder, IMAGE *image); + CODEC_ERROR ImageUnpackingProcess(const PACKED_IMAGE *packed_image, + UNPACKED_IMAGE *unpacked_image, + const ENCODER_PARAMETERS *parameters, + gpr_allocator *allocator); + + CODEC_ERROR UnpackImage(const PACKED_IMAGE *input, UNPACKED_IMAGE *output, ENABLED_PARTS enabled_parts); + + CODEC_ERROR PreprocessImageRow(uint8_t *input, DIMENSION image_width, uint8_t *output); + + CODEC_ERROR UnpackImageRow(uint8_t *input_row_ptr, + DIMENSION image_width, + PIXEL_FORMAT pixel_format, + PIXEL *output_row_ptr[], + PRECISION bits_per_component[], + int channel_count, + ENABLED_PARTS enabled_parts, + int raw_shift); + + CODEC_ERROR EncodeBitstreamHeader(ENCODER *encoder, BITSTREAM *bitstream); + + CODEC_ERROR EncodeBitstreamTrailer(ENCODER *encoder, BITSTREAM *bitstream); + + CODEC_ERROR EncodeExtensionHeader(ENCODER *encoder, BITSTREAM *bitstream); + + CODEC_ERROR EncodeExtensionTrailer(ENCODER *encoder, BITSTREAM *bitstream); + + //CODEC_ERROR EncodeLayer(ENCODER *encoder, void *buffer, size_t pitch, BITSTREAM *stream); + CODEC_ERROR EncodeMultipleChannels(ENCODER *encoder, const UNPACKED_IMAGE *image, BITSTREAM *stream); + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + CODEC_ERROR EncodeLayerHeader(ENCODER *encoder, BITSTREAM *bitstream); + CODEC_ERROR EncodeLayerTrailer(ENCODER *encoder, BITSTREAM *bitstream); +#endif + + CODEC_ERROR SetEncoderQuantization(ENCODER *encoder, + const ENCODER_PARAMETERS *parameters); + + CODEC_ERROR SetTransformQuantTable(ENCODER *encoder, int channel, const QUANT table[], int length); + + CODEC_ERROR GetChannelDimensions(ENCODER *encoder, + int channel_number, + DIMENSION *channel_width_out, + DIMENSION *channel_height_out); + + CODEC_ERROR GetMaximumChannelDimensions(const UNPACKED_IMAGE *image, DIMENSION *width_out, DIMENSION *height_out); + + DIMENSION EncodedLayerHeight(ENCODER *encoder, DIMENSION height); + + CODEC_ERROR SetEncodedBandMask(CODEC_STATE *codec, int subband); + + CODEC_ERROR EncodeChannelSubbands(ENCODER *encoder, int channel, BITSTREAM *stream); + + CODEC_ERROR EncodeChannelHeader(ENCODER *encoder, + int channel_number, + BITSTREAM *stream); + + CODEC_ERROR EncodeChannelTrailer(ENCODER *encoder, int channel, BITSTREAM *stream); + + //CODEC_ERROR EncodeLayerChannels(ENCODER *encoder, BITSTREAM *stream); + CODEC_ERROR EncodeChannelWavelets(ENCODER *encoder, BITSTREAM *stream); + + CODEC_ERROR PutVideoLowpassHeader(ENCODER *encoder, int channel_number, BITSTREAM *stream); + + CODEC_ERROR PutVideoSubbandHeader(ENCODER *encoder, int subband, QUANT quantization, BITSTREAM *stream); + CODEC_ERROR PutVideoSubbandTrailer(ENCODER *encoder, BITSTREAM *stream); + + CODEC_ERROR TransformForwardSpatialQuantFrame(ENCODER *encoder, void *buffer, size_t pitch); + + CODEC_ERROR AllocateEncoderHorizontalBuffers(ENCODER *encoder); + + CODEC_ERROR DeallocateEncoderHorizontalBuffers(ENCODER *encoder); + + CODEC_ERROR AllocateEncoderUnpackingBuffers(ENCODER *encoder, int frame_width); + + CODEC_ERROR DeallocateEncoderUnpackingBuffers(ENCODER *encoder); + + CODEC_ERROR AllocateHorizontalBuffers(gpr_allocator *allocator, + PIXEL *lowpass_buffer[], + PIXEL *highpass_buffer[], + int buffer_width); + + CODEC_ERROR DeallocateHorizontalBuffers(gpr_allocator *allocator, + PIXEL *lowpass_buffer[], + PIXEL *highpass_buffer[]); + + + CODEC_ERROR PadWaveletBands(ENCODER *encoder, WAVELET *wavelet); + + CODEC_ERROR EncodeLowpassBand(ENCODER *encoder, WAVELET *wavelet, int channel_number, BITSTREAM *stream); + + CODEC_ERROR EncodeHighpassBand(ENCODER *encoder, WAVELET *wavelet, int band, int subband, BITSTREAM *stream); + + CODEC_ERROR EncodeHighpassBandLongRuns(BITSTREAM *stream, ENCODER_CODESET *codeset, PIXEL *data, + DIMENSION width, DIMENSION height, DIMENSION pitch); + + CODEC_ERROR EncodeHighpassBandRowRuns(BITSTREAM *stream, ENCODER_CODESET *codeset, PIXEL *data, + DIMENSION width, DIMENSION height, DIMENSION pitch); + + CODEC_ERROR GetSampleOffsetSegment(BITSTREAM *bitstream, uint32_t offset, TAGVALUE *segment_out); + +#ifdef __cplusplus +} +#endif + +#endif // ENCODER_H diff --git a/source/lib/vc5_encoder/forward.c b/source/lib/vc5_encoder/forward.c new file mode 100755 index 0000000..ab54dd8 --- /dev/null +++ b/source/lib/vc5_encoder/forward.c @@ -0,0 +1,851 @@ +/*! @file forward.c + * + * @brief Implementation of the forward wavelet transform functions. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +#if ENABLED(NEON) +#include +#endif + +//! Rounding added to the highpass sum before division +static const int32_t rounding = 4; + +STATIC_INLINE PIXEL QuantizeValue(int16_t value, int32_t midpoint, int32_t multiplier ) +{ + int less_than_zero = 0; + int negate_less_than_zero = 0; + + int16_t x = abs(value) + midpoint; + + if( value < 0 ) + { + less_than_zero = -1; + negate_less_than_zero = 1; + } + + x = (int32_t)(x * multiplier) >> 16; + + x = x ^ less_than_zero; + x = x + negate_less_than_zero; + + return ClampPixel(x); +} + +static void FilterVerticalTopBottom_Core_8x_C_(PIXEL *coefficients[], int column, int16_t* highpass, int16_t* lowpass, bool top ) +{ + const int filter_coeffs_top[] = { 5, -11, 4, 4, -1, -1 }; + const int filter_coeffs_bottom[] = { 1, 1, -4, -4, 11, -5 }; + + int low_band_index = top ? 0 : 4; + const int* filter_coeffs = top ? filter_coeffs_top : filter_coeffs_bottom; + + int i, f; + + for (i = 0; i < 8; i++) + { + lowpass[i] = coefficients[low_band_index + 0][column + i] + coefficients[low_band_index + 1][column + i]; + } + + for (i = 0; i < 8; i++) + { + int32_t sum = 0; + + for (f = 0; f < 6; f++) + { + sum += filter_coeffs[f] * coefficients[f][column + i]; + } + + sum += rounding; + sum = DivideByShift(sum, 3); + highpass[i] = sum; + } +} + +#if ENABLED(NEON) + +static const uint16x8_t mask = {0x0000, 0xFFFF,0x0000,0xFFFF,0x0000,0xFFFF,0x0000, 0xFFFF}; + +#define HorizontalFilter_Prescale2_4x HorizontalFilter_Prescale2_4x_NEON_ +void HorizontalFilter_Prescale2_4x_NEON_(PIXEL *input, PIXEL* lowpass, PIXEL* highpass ) +{ + const int prescale_rounding = 3; + const int prescale = 2; + + int32x4_t __pairwise_sum_0_7, __highpass; + int32x4_t __diff; + int16x8_t __input_2_9; + + { + const int16x8_t __prescale_rounding = vdupq_n_s16 (prescale_rounding); + const int16x8_t __shift = vdupq_n_s16 (-prescale); + + int16x8_t __input_0_7 = vld1q_s16( input ); + __input_2_9 = vld1q_s16( input + 2 ); + int16x8_t __input_8_15 = vld1q_s16( input + 8 ); + + __input_0_7 = vaddq_s16( __input_0_7, __prescale_rounding ); + __input_0_7 = vshlq_s16( __input_0_7, __shift ); + + __input_8_15 = vaddq_s16( __input_8_15, __prescale_rounding ); + __input_8_15 = vshlq_s16( __input_8_15, __shift ); + + __pairwise_sum_0_7 = vpaddlq_s16(__input_0_7); + int32x4_t __pairwise_sum_8_15 = vpaddlq_s16(__input_8_15); + + __input_0_7 = vbslq_s16(mask, vnegq_s16(__input_0_7), __input_0_7); + __input_8_15 = vbslq_s16(mask, vnegq_s16(__input_8_15), __input_8_15); + __diff = vextq_s32(vpaddlq_s16( __input_0_7 ), vpaddlq_s16( __input_8_15 ), 1); + + __highpass = vcombine_s32( vget_high_s32(__pairwise_sum_0_7), vget_low_s32(__pairwise_sum_8_15) ); + } + + // High pass band + { + const int32x4_t __rounding = vdupq_n_s32(rounding); + + __highpass = vsubq_s32( __highpass, __pairwise_sum_0_7 ); + __highpass = vaddq_s32( __highpass, __rounding ); + __highpass = vshrq_n_s32( __highpass, 3 ); + __highpass = vqaddq_s32( __highpass, __diff ); // Dont need to clamp because we are using saturating instruction + + vst1_s16(highpass, vmovn_s32(__highpass) ); + } + + // Low pass band + { + const int32x4_t __prescale_rounding = vdupq_n_s32(prescale_rounding); + const int32x4_t __shift = vdupq_n_s32(-prescale); + + int32x4_t __pairwise_sum_2_9 = vpaddlq_s16(__input_2_9); + + __pairwise_sum_2_9 = vaddq_s32(__pairwise_sum_2_9, __prescale_rounding); + __pairwise_sum_2_9 = vshlq_s32(__pairwise_sum_2_9, __shift); + + vst1_s16(lowpass, vmovn_s32(__pairwise_sum_2_9) ); + } +} + +#define HorizontalFilter_Prescale0_4x HorizontalFilter_Prescale0_4x_NEON_ +void HorizontalFilter_Prescale0_4x_NEON_(PIXEL *input, PIXEL* lowpass, PIXEL* highpass ) +{ + int32x4_t __pairwise_sum_0_7, __highpass; + int32x4_t __diff; + int16x8_t __input_2_9; + + { + int16x8_t __input_0_7 = vld1q_s16( input ); + __input_2_9 = vld1q_s16( input + 2 ); + int16x8_t __input_8_15 = vld1q_s16( input + 8 ); + + __pairwise_sum_0_7 = vpaddlq_s16(__input_0_7); + int32x4_t __pairwise_sum_8_15 = vpaddlq_s16(__input_8_15); + + __input_0_7 = vbslq_s16(mask, vnegq_s16(__input_0_7), __input_0_7); + __input_8_15 = vbslq_s16(mask, vnegq_s16(__input_8_15), __input_8_15); + __diff = vextq_s32(vpaddlq_s16( __input_0_7 ), vpaddlq_s16( __input_8_15 ), 1); + + __highpass = vcombine_s32( vget_high_s32(__pairwise_sum_0_7), vget_low_s32(__pairwise_sum_8_15) ); + } + + // High pass band + { + const int32x4_t __rounding = vdupq_n_s32(rounding); + + __highpass = vsubq_s32( __highpass, __pairwise_sum_0_7 ); + __highpass = vaddq_s32( __highpass, __rounding ); + __highpass = vshrq_n_s32( __highpass, 3 ); + __highpass = vqaddq_s32( __highpass, __diff ); // Dont need to clamp because we are using saturating instruction + + vst1_s16(highpass, vmovn_s32(__highpass) ); + } + + // Low pass band + { + int32x4_t __pairwise_sum_2_9 = vpaddlq_s16(__input_2_9); + + vst1_s16(lowpass, vmovn_s32(__pairwise_sum_2_9) ); + } +} + +void QuantizeBand_8x_NEON_(int16_t* wavelet_band, int16_t midpoint, int32_t multiplier, PIXEL *output ) +{ + int16x8_t __wavelet_band = vld1q_s16( wavelet_band ); + + int16x8_t __wavelet_band_abs = vaddq_s16( vabsq_s16(__wavelet_band), vdupq_n_s16( midpoint ) ); + + int32x4_t __multipliers = vdupq_n_s32( multiplier ); + + int32x4_t __value_high = vmovl_s16( vget_high_s16(__wavelet_band_abs) ); + __value_high = vmulq_s32( __value_high, __multipliers ); + + int32x4_t __value_low = vmovl_s16( vget_low_s16(__wavelet_band_abs) ); + __value_low = vmulq_s32( __value_low, __multipliers ); + + int16x8_t __multiplied = vcombine_s16( vshrn_n_s32( __value_low, 16 ), vshrn_n_s32( __value_high, 16 ) ); + + uint16x8_t mask = vcltq_s16(__wavelet_band, vdupq_n_s16(0) ); + int16x8_t __neg_output = vnegq_s16(__multiplied); + + int16x8_t __result = vbslq_s16( mask, __neg_output, __multiplied ); + + vst1q_s16(output, __result); +} + +void FilterVerticalMiddle_Core_8x_NEON_(PIXEL *coefficients[], int column, int16_t* highpass, int16_t* lowpass ) +{ + int16x8_t __highpass, __highpass_50, __highpass_14; + + { + int16x8_t __row_0 = vld1q_s16( &coefficients[0][column] ); + int16x8_t __row_5 = vld1q_s16( &coefficients[5][column] ); + + __highpass_50 = vsubq_s16( __row_5, __row_0 ); + } + + { + int16x8_t __row_1 = vld1q_s16( &coefficients[1][column] ); + int16x8_t __row_4 = vld1q_s16( &coefficients[4][column] ); + + __highpass_14 = vsubq_s16( __row_4, __row_1 ); + } + + { + int16x8_t __rounding = vdupq_n_s16 (rounding); + + __highpass = vaddq_s16( __highpass_50, __highpass_14 ); + __highpass = vaddq_s16( __highpass, __rounding ); + __highpass = vshrq_n_s16(__highpass, 3); + } + + { + int16x8_t __row_2 = vld1q_s16( &coefficients[2][column] ); + int16x8_t __row_3 = vld1q_s16( &coefficients[3][column] ); + + int16x8_t __diff_23 = vsubq_s16( __row_2, __row_3 ); + int16x8_t __sum_23 = vaddq_s16( __row_2, __row_3 ); + + __highpass = vaddq_s16( __highpass, __diff_23 ); + + vst1q_s16(lowpass, __sum_23); + } + + vst1q_s16(highpass, __highpass); + +} + +#define FilterVerticalMiddle_8x FilterVerticalMiddle_8x_NEON_ +void FilterVerticalMiddle_8x_NEON_(PIXEL *lowpass[], PIXEL *highpass[], int column, int32_t* midpoints, int32_t* multipliers, PIXEL *result[]) +{ + int16_t LOW[8]; + int16_t HIGH[8]; + + FilterVerticalMiddle_Core_8x_NEON_( highpass, column, HIGH, LOW); + QuantizeBand_8x_NEON_( LOW, midpoints[LH_BAND], multipliers[LH_BAND], result[LH_BAND] + column ); + QuantizeBand_8x_NEON_( HIGH, midpoints[HH_BAND], multipliers[HH_BAND], result[HH_BAND] + column ); + + FilterVerticalMiddle_Core_8x_NEON_( lowpass, column, HIGH, LOW); + QuantizeBand_8x_NEON_( LOW, midpoints[LL_BAND], multipliers[LL_BAND], result[LL_BAND] + column ); + QuantizeBand_8x_NEON_( HIGH, midpoints[HL_BAND], multipliers[HL_BAND], result[HL_BAND] + column ); +} + +#define FilterVerticalTopBottom_8x FilterVerticalTopBottom_8x_NEON_ +void FilterVerticalTopBottom_8x_NEON_(PIXEL *lowpass[], PIXEL *highpass[], int column, int32_t* midpoints, int32_t* multipliers, PIXEL *result[], bool top ) +{ + int16_t LOW[8]; + int16_t HIGH[8]; + + FilterVerticalTopBottom_Core_8x_C_( highpass, column, HIGH, LOW, top ); + QuantizeBand_8x_NEON_( LOW, midpoints[LH_BAND], multipliers[LH_BAND], result[LH_BAND] + column ); + QuantizeBand_8x_NEON_( HIGH, midpoints[HH_BAND], multipliers[HH_BAND], result[HH_BAND] + column ); + + FilterVerticalTopBottom_Core_8x_C_( lowpass, column, HIGH, LOW, top ); + QuantizeBand_8x_NEON_( LOW, midpoints[LL_BAND], multipliers[LL_BAND], result[LL_BAND] + column ); + QuantizeBand_8x_NEON_( HIGH, midpoints[HL_BAND], multipliers[HL_BAND], result[HL_BAND] + column ); +} + +#else + +#define HorizontalFilter_Prescale2_4x HorizontalFilter_Prescale2_4x_C_ +void HorizontalFilter_Prescale2_4x_C_(PIXEL *input, PIXEL* lowpass, PIXEL* highpass ) +{ + int i; + PIXEL input_c[16]; + for ( i = 0; i < 12; i++) + { + input_c[i] = (input[i] + 3) >> 2; + } + + int32_t diff_23 = input_c[2] - input_c[3]; + int32_t diff_45 = input_c[4] - input_c[5]; + int32_t diff_67 = input_c[6] - input_c[7]; + int32_t diff_89 = input_c[8] - input_c[9]; + + int32_t sum_01 = input_c[0] + input_c[1]; + int32_t sum_23 = input_c[2] + input_c[3]; + int32_t sum_45 = input_c[4] + input_c[5]; + int32_t sum_67 = input_c[6] + input_c[7]; + int32_t sum_89 = input_c[8] + input_c[9]; + int32_t sum_1011 = input_c[10] + input_c[11]; + + { + int32_t sum = sum_45 - sum_01; + + sum += rounding; + sum = DivideByShift(sum, 3); + sum += diff_23; + + highpass[0] = ClampPixel(sum); + } + + { + int32_t sum = sum_67 - sum_23; + + sum += rounding; + sum = DivideByShift(sum, 3); + sum += diff_45; + + highpass[1] = ClampPixel(sum); + } + + { + int32_t sum = sum_89 - sum_45; + + sum += rounding; + sum = DivideByShift(sum, 3); + sum += diff_67; + + highpass[2] = ClampPixel(sum); + } + + { + int32_t sum = sum_1011 - sum_67; + + sum += rounding; + sum = DivideByShift(sum, 3); + sum += diff_89; + + highpass[3] = ClampPixel(sum); + } + + lowpass[0] = (input[2] + input[3] + 3) >> 2; + lowpass[1] = (input[4] + input[5] + 3) >> 2; + lowpass[2] = (input[6] + input[7] + 3) >> 2; + lowpass[3] = (input[8] + input[9] + 3) >> 2; +} + +#define HorizontalFilter_Prescale0_4x HorizontalFilter_Prescale0_4x_C_ +void HorizontalFilter_Prescale0_4x_C_(PIXEL *input, PIXEL* lowpass, PIXEL* highpass ) +{ + PIXEL input_c[16]; + + memcpy(input_c, input, sizeof(PIXEL) * 12); + + int32_t diff_23 = input_c[2] - input_c[3]; + int32_t diff_45 = input_c[4] - input_c[5]; + int32_t diff_67 = input_c[6] - input_c[7]; + int32_t diff_89 = input_c[8] - input_c[9]; + + int32_t sum_01 = input_c[0] + input_c[1]; + int32_t sum_23 = input_c[2] + input_c[3]; + int32_t sum_45 = input_c[4] + input_c[5]; + int32_t sum_67 = input_c[6] + input_c[7]; + int32_t sum_89 = input_c[8] + input_c[9]; + int32_t sum_1011 = input_c[10] + input_c[11]; + + { + int32_t sum = sum_45 - sum_01; + + sum += rounding; + sum = DivideByShift(sum, 3); + sum += diff_23; + + highpass[0] = ClampPixel(sum); + } + + { + int32_t sum = sum_67 - sum_23; + + sum += rounding; + sum = DivideByShift(sum, 3); + sum += diff_45; + + highpass[1] = ClampPixel(sum); + } + + { + int32_t sum = sum_89 - sum_45; + + sum += rounding; + sum = DivideByShift(sum, 3); + sum += diff_67; + + highpass[2] = ClampPixel(sum); + } + + { + int32_t sum = sum_1011 - sum_67; + + sum += rounding; + sum = DivideByShift(sum, 3); + sum += diff_89; + + highpass[3] = ClampPixel(sum); + } + + lowpass[0] = input[2] + input[3]; + lowpass[1] = input[4] + input[5]; + lowpass[2] = input[6] + input[7]; + lowpass[3] = input[8] + input[9]; +} + +void FilterVerticalMiddle_Core_8x_C_(PIXEL *coefficients[], int column, int16_t* highpass, int16_t* lowpass ) +{ + PIXEL row_0[8]; + PIXEL row_1[8]; + PIXEL row_2[8]; + PIXEL row_3[8]; + PIXEL row_4[8]; + PIXEL row_5[8]; + + int i; + + memcpy( row_0, &coefficients[0][column], sizeof(PIXEL) * 8 ); + memcpy( row_1, &coefficients[1][column], sizeof(PIXEL) * 8 ); + memcpy( row_2, &coefficients[2][column], sizeof(PIXEL) * 8 ); + memcpy( row_3, &coefficients[3][column], sizeof(PIXEL) * 8 ); + memcpy( row_4, &coefficients[4][column], sizeof(PIXEL) * 8 ); + memcpy( row_5, &coefficients[5][column], sizeof(PIXEL) * 8 ); + + for (i = 0; i < 8; i++) + highpass[i] = (-1 * row_0[i] + row_5[i]); + + for (i = 0; i < 8; i++) + highpass[i] += (-1 * row_1[i] + row_4[i]); + + for (i = 0; i < 8; i++) + highpass[i] += rounding; + + for (i = 0; i < 8; i++) + highpass[i] = DivideByShift(highpass[i], 3); + + for (i = 0; i < 8; i++) + { + highpass[i] += (row_2[i] - row_3[i]); + lowpass[i] = (row_2[i] + row_3[i]); + } +} + + +#define FilterVerticalMiddle_8x FilterVerticalMiddle_8x_C_ +void FilterVerticalMiddle_8x_C_(PIXEL *lowpass[], PIXEL *highpass[], int column, int32_t* midpoints, int32_t* multipliers, PIXEL *result[]) +{ + int i; + + int16_t LL[8]; + int16_t HL[8]; + int16_t LH[8]; + int16_t HH[8]; + + FilterVerticalMiddle_Core_8x_C_( highpass, column, HH, LH); + FilterVerticalMiddle_Core_8x_C_( lowpass, column, HL, LL); + + for (i = 0; i < 8; i++) + { + result[LL_BAND][column + i] = QuantizeValue( LL[i], midpoints[LL_BAND], multipliers[LL_BAND] ); + result[LH_BAND][column + i] = QuantizeValue( LH[i], midpoints[LH_BAND], multipliers[LH_BAND] ); + result[HL_BAND][column + i] = QuantizeValue( HL[i], midpoints[HL_BAND], multipliers[HL_BAND] ); + result[HH_BAND][column + i] = QuantizeValue( HH[i], midpoints[HH_BAND], multipliers[HH_BAND] ); + } +} + +#define FilterVerticalTopBottom_8x FilterVerticalTopBottom_8x_C_ +void FilterVerticalTopBottom_8x_C_(PIXEL *lowpass[], PIXEL *highpass[], int column, int32_t* midpoints, int32_t* multipliers, PIXEL *result[], bool top ) +{ + int i; + + int16_t LL[8]; + int16_t HL[8]; + int16_t LH[8]; + int16_t HH[8]; + + FilterVerticalTopBottom_Core_8x_C_( highpass, column, HH, LH, top ); + FilterVerticalTopBottom_Core_8x_C_( lowpass, column, HL, LL, top ); + + for (i = 0; i < 8; i++) + { + result[LL_BAND][column + i] = QuantizeValue( LL[i], midpoints[LL_BAND], multipliers[LL_BAND] ); + result[LH_BAND][column + i] = QuantizeValue( LH[i], midpoints[LH_BAND], multipliers[LH_BAND] ); + result[HL_BAND][column + i] = QuantizeValue( HL[i], midpoints[HL_BAND], multipliers[HL_BAND] ); + result[HH_BAND][column + i] = QuantizeValue( HH[i], midpoints[HH_BAND], multipliers[HH_BAND] ); + } +} + +#endif + +static PIXEL HorizontalHighPassFilter_Middle(PIXEL *input, int prescale_rounding, int prescale) +{ + int32_t sum; + + if( prescale == 0 ) + { + sum = -input[0] - input[1] + (input[2] << 3) - (input[3] << 3) + input[4] + input[5]; + } + else + { + sum = 0; + sum -= (input[0] + prescale_rounding) >> prescale; + sum -= (input[1] + prescale_rounding) >> prescale; + sum += ((input[2] + prescale_rounding) >> prescale) << 3; + sum -= ((input[3] + prescale_rounding) >> prescale) << 3; + sum += (input[4] + prescale_rounding) >> prescale; + sum += (input[5] + prescale_rounding) >> prescale; + } + + sum += rounding; + sum = DivideByShift(sum, 3); + return ClampPixel(sum); +} + +static PIXEL HorizontalHighPassFilter(PIXEL *input, PIXEL *multipliers, int prescale_rounding, int prescale) +{ + int i; + int32_t sum = 0; + + if( prescale == 0 ) + { + for (i = 0; i < 6; i++) + { + sum += multipliers[i] * input[i]; + } + + } + else + { + for (i = 0; i < 6; i++) + { + sum += multipliers[i] * ( (input[i] + prescale_rounding) >> prescale); + } + } + + sum += rounding; + sum = DivideByShift(sum, 3); + return ClampPixel(sum); +} + +/*! + @brief Apply the horizontal wavelet filter to a row of pixels +*/ +CODEC_ERROR FilterHorizontalRow(PIXEL *input, PIXEL *lowpass, PIXEL *highpass, int width, int prescale) +{ + int column = 2; + + //uint16_t *input = (uint16_t *)input_buffer; + + //TODO: Check that the rounding is correct for all prescale values + int prescale_rounding = (1 << prescale) - 1; + + const int last_input_column = ((width % 2) == 0) ? width - 2 : width - 1; + + int last_input_column_tight = ( (last_input_column - 4) / 8) * 8; + + //TODO Test this routine with other prescale values + assert(prescale == 0 || prescale == 2); + + /***** Process the left border using the formula for boundary conditions *****/ + + // Compute the lowpass coefficient + lowpass[0] = (input[0] + input[1] + prescale_rounding) >> prescale; + + { + PIXEL coefficients[6] = { 5, -11, 4, 4, -1, -1 }; + highpass[0] = HorizontalHighPassFilter(input, coefficients, prescale_rounding, prescale ); + } + + if( prescale == 2 ) + { + /***** Process the internal pixels using the normal wavelet formula *****/ + for (; column < last_input_column_tight; column += 8) // + { + // Column index should always be divisible by two + assert((column % 2) == 0); + + HorizontalFilter_Prescale2_4x( input + column - 2, &lowpass[column/2], &highpass[column/2] ); + } + } + else if( prescale == 0 ) + { + /***** Process the internal pixels using the normal wavelet formula *****/ + for (; column < last_input_column_tight; column += 8) // + { + // Column index should always be divisible by two + assert((column % 2) == 0); + + HorizontalFilter_Prescale0_4x( input + column - 2, &lowpass[column/2], &highpass[column/2] ); + } + } + else + { + assert(0); + } + + for (; column < last_input_column; column += 2) + { + // Column index should always be divisible by two + assert((column % 2) == 0); + + // Compute the lowpass coefficient + lowpass[column/2] = (input[column + 0] + input[column + 1] + prescale_rounding) >> prescale; + + // Initialize the sum for computing the highpass coefficient + if ((column + 3) < width) + { + highpass[column/2] = HorizontalHighPassFilter_Middle(input + column - 2, prescale_rounding, prescale ); + } + else + { + int32_t sum = 0; + + sum -= (input[column - 2] + prescale_rounding) >> prescale; + sum -= (input[column - 1] + prescale_rounding) >> prescale; + sum += (input[column + 2] + prescale_rounding) >> prescale; + + // Duplicate the value in the last column + sum += (input[column + 2] + prescale_rounding) >> prescale; + sum += rounding; + sum = DivideByShift(sum, 3); + sum += (input[column + 0] + prescale_rounding) >> prescale; + sum -= (input[column + 1] + prescale_rounding) >> prescale; + highpass[column/2] = ClampPixel(sum); + } + } + + // Should have exited the loop at the last column + assert(column == last_input_column); + + /***** Process the right border using the formula for boundary conditions *****/ + + // Compute the lowpass coefficient + if ((column + 1) < width) + { + PIXEL coefficients[6] = { 1, 1, -4, -4, 11, -5 }; + highpass[column/2] = HorizontalHighPassFilter(input + column - 4, coefficients, prescale_rounding, prescale ); + + // Use the value in the last column + lowpass[column/2] = (input[column + 0] + input[column + 1] + prescale_rounding) >> prescale; + } + else + { + int32_t sum = 0; + // Duplicate the value in the last column + sum -= 5 * ((input[column + 0] + prescale_rounding) >> prescale); + + sum += 11 * ((input[column + 0] + prescale_rounding) >> prescale); + sum -= 4 * ((input[column - 1] + prescale_rounding) >> prescale); + sum -= 4 * ((input[column - 2] + prescale_rounding) >> prescale); + sum += 1 * ((input[column - 3] + prescale_rounding) >> prescale); + sum += 1 * ((input[column - 4] + prescale_rounding) >> prescale); + sum += rounding; + sum = DivideByShift(sum, 3); + + highpass[column/2] = ClampPixel(sum); + + // Duplicate the value in the last column + lowpass[column/2] = (input[column + 0] + input[column + 0] + prescale_rounding) >> prescale; + } + + return CODEC_ERROR_OKAY; +} + +static void FilterVerticalMiddle_1x(PIXEL *lowpass[], PIXEL *highpass[], int column, int32_t* midpoints, int32_t* multipliers, PIXEL *result[]) +{ + { + const PIXEL coefficients_01 = lowpass[0][column] + lowpass[1][column]; + const PIXEL coefficients_2 = lowpass[2][column]; + const PIXEL coefficients_3 = lowpass[3][column]; + const PIXEL coefficients_45 = lowpass[4][column] + lowpass[5][column]; + + int32_t sum = coefficients_45 - coefficients_01; + sum += 8 * (coefficients_2 - coefficients_3); + sum = DivideByShift(sum + rounding, 3); + + result[LL_BAND][column] = QuantizeValue( coefficients_2 + coefficients_3, midpoints[LL_BAND], multipliers[LL_BAND] ); + result[HL_BAND][column] = QuantizeValue( sum, midpoints[HL_BAND], multipliers[HL_BAND] ); + } + + { + const PIXEL coefficients_01 = highpass[0][column] + highpass[1][column]; + const PIXEL coefficients_2 = highpass[2][column]; + const PIXEL coefficients_3 = highpass[3][column]; + const PIXEL coefficients_45 = highpass[4][column] + highpass[5][column]; + + int32_t sum = coefficients_45 - coefficients_01; + sum += 8 * (coefficients_2 - coefficients_3); + sum = DivideByShift(sum + rounding, 3); + + result[LH_BAND][column] = QuantizeValue( coefficients_2 + coefficients_3, midpoints[LH_BAND], multipliers[LH_BAND] ); + result[HH_BAND][column] = QuantizeValue( sum, midpoints[HH_BAND], multipliers[HH_BAND] ); + } +} + +static void FilterVerticalTopBottom_1x(PIXEL *lowpass[], PIXEL *highpass[], int column, int32_t* midpoints, int32_t* multipliers, PIXEL *result[], bool top ) +{ + const int filter_coeffs_top[] = { 5, -11, 4, 4, -1, -1 }; + const int filter_coeffs_bottom[] = { 1, 1, -4, -4, 11, -5 }; + + int low_band_index = top ? 0 : 4; + const int* filter_coeffs = top ? filter_coeffs_top : filter_coeffs_bottom; + + int i; + int32_t sum_L = 0; + int32_t sum_H = 0; + + // Apply the lowpass vertical filter to the lowpass horizontal results + result[LL_BAND][column] = QuantizeValue( lowpass[low_band_index + 0][column] + lowpass[low_band_index + 1][column], midpoints[LL_BAND], multipliers[LL_BAND] ); + + // Apply the lowpass vertical filter to the highpass horizontal results + result[LH_BAND][column] = QuantizeValue( highpass[low_band_index + 0][column] + highpass[low_band_index + 1][column], midpoints[LH_BAND], multipliers[LH_BAND] ); + + for (i = 0; i < 6; i++) + { + sum_L += filter_coeffs[i] * lowpass[i][column]; + sum_H += filter_coeffs[i] * highpass[i][column]; + } + + sum_L += rounding; + sum_L = DivideByShift(sum_L, 3); + result[HL_BAND][column] = QuantizeValue( sum_L, midpoints[HL_BAND], multipliers[HL_BAND] ); + + sum_H += rounding; + sum_H = DivideByShift(sum_H, 3); + result[HH_BAND][column] = QuantizeValue( sum_H, midpoints[HH_BAND], multipliers[HH_BAND] ); +} + +/*! + @brief Apply the vertical wavelet filter to the first row + + This routine uses the wavelet formulas for the top row of an image + + The midpoint prequant argument is not the offset that is added to the + value prior to quantization. It is a setting indicating which midpoint + offset to use. + + @todo Change the midpoint_prequant argument to midpoint_setting? +*/ +CODEC_ERROR FilterVerticalTopRow(PIXEL **lowpass, PIXEL **highpass, PIXEL **output, int wavelet_width, int wavelet_pitch, int32_t midpoints[], int32_t multipliers[], int input_row ) +{ + int column = 0; + const int wavelet_width_m8 = (wavelet_width / 8) * 8; + + assert(input_row == 0); + + for (; column < wavelet_width_m8; column += 8) + { + FilterVerticalTopBottom_8x( lowpass, highpass, column, midpoints, multipliers, output, true ); + + assert(output[LL_BAND][column] >= 0); + } + + for (; column < wavelet_width; column++) + { + FilterVerticalTopBottom_1x( lowpass, highpass, column, midpoints, multipliers, output, true ); + + assert(output[LL_BAND][column] >= 0); + } + + return CODEC_ERROR_OKAY; +} + + +CODEC_ERROR FilterVerticalBottomRow(PIXEL **lowpass, PIXEL **highpass, PIXEL **output, int wavelet_width, int wavelet_pitch, int32_t midpoints[], int32_t multipliers[], int input_row ) +{ + PIXEL *result[MAX_BAND_COUNT]; + int column = 0; + const int wavelet_width_m8 = (wavelet_width / 8) * 8; + + int band; + + //uint16_t **lowpass = (uint16_t **)lowpass_buffer; + + int output_row = input_row / 2; + + // Compute the address of each output row + for (band = 0; band < MAX_BAND_COUNT; band++) + { + uint8_t *band_row_ptr = (uint8_t *)output[band]; + band_row_ptr += output_row * wavelet_pitch; + result[band] = (PIXEL *)band_row_ptr; + } + + for (; column < wavelet_width_m8; column += 8) + { + FilterVerticalTopBottom_8x( lowpass, highpass, column, midpoints, multipliers, result, false ); + + assert(result[LL_BAND][column] >= 0); + } + + for (; column < wavelet_width; column++) + { + FilterVerticalTopBottom_1x( lowpass, highpass, column, midpoints, multipliers, result, false ); + + assert(result[LL_BAND][column] >= 0); + } + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Apply the vertical wavelet filter to a middle row + + This routine uses the wavelet formulas for the middle rows of an image +*/ +CODEC_ERROR FilterVerticalMiddleRow(PIXEL **lowpass, PIXEL **highpass, PIXEL **output, int wavelet_width, int wavelet_pitch, int32_t midpoints[], int32_t multipliers[], int input_row ) +{ + PIXEL *result[MAX_BAND_COUNT]; + + int column = 0; + int band; + + const int wavelet_width_m8 = (wavelet_width / 8) * 8; + + //uint16_t **lowpass = (uint16_t **)lowpass_buffer; + + int output_row = input_row / 2; + + // Compute the address of each output row + for (band = 0; band < MAX_BAND_COUNT; band++) + { + uint8_t *band_row_ptr = (uint8_t *)output[band]; + band_row_ptr += output_row * wavelet_pitch; + result[band] = (PIXEL *)band_row_ptr; + } + + for (; column < wavelet_width_m8; column += 8) + { + FilterVerticalMiddle_8x(lowpass, highpass, column, midpoints, multipliers, result); + } + + for (; column < wavelet_width; column += 1) + { + FilterVerticalMiddle_1x(lowpass, highpass, column, midpoints, multipliers, result); + + assert(result[LL_BAND][column] >= 0); + } + + return CODEC_ERROR_OKAY; +} + diff --git a/source/lib/vc5_encoder/forward.h b/source/lib/vc5_encoder/forward.h new file mode 100755 index 0000000..74e2369 --- /dev/null +++ b/source/lib/vc5_encoder/forward.h @@ -0,0 +1,38 @@ +/*! @file forward.h + * + * @brief Declare of the forward wavelet transform functions. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef FORWARD_H +#define FORWARD_H + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR FilterHorizontalRow(PIXEL *input, PIXEL *lowpass, PIXEL *highpass, int width, int prescale); + + CODEC_ERROR FilterVerticalTopRow(PIXEL **lowpass, PIXEL **highpass, PIXEL **output, int wavelet_width, int wavelet_pitch, int32_t midpoints[], int32_t multipliers[], int input_row ); + + CODEC_ERROR FilterVerticalMiddleRow(PIXEL **lowpass, PIXEL **highpass, PIXEL **output, int wavelet_width, int wavelet_pitch, int32_t midpoints[], int32_t multipliers[], int input_row ); + + CODEC_ERROR FilterVerticalBottomRow(PIXEL **lowpass, PIXEL **highpass, PIXEL **output, int wavelet_width, int wavelet_pitch, int32_t midpoints[], int32_t multipliers[], int input_row ); + +#ifdef __cplusplus +} +#endif + +#endif // FORWARD_H diff --git a/source/lib/vc5_encoder/headers.h b/source/lib/vc5_encoder/headers.h new file mode 100755 index 0000000..bdf8ad9 --- /dev/null +++ b/source/lib/vc5_encoder/headers.h @@ -0,0 +1,50 @@ +/*! @file headers.h + * + * @brief This file includes all of the header files that are used by the encoder. + * + * Note that some header files are only used by the main program that + * calls the codec or are only used for debugging are not included by this file. + * Only headers that are part of the reference encoder are included by this file. + * + * Including a single header file in all reference encoder source files + * ensures that all modules see the same header files in the same order. + * + * This file can be used for creating a pre-compiled header if the + * compiler supports that capabilities. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef HEADERS_H +#define HEADERS_H + +#include "common.h" + +#include "vlc.h" +#include "bitstream.h" +#include "raw.h" +#include "codebooks.h" +#include "component.h" +#include "syntax.h" +#include "forward.h" + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) +#include "sections.h" +#endif + +#include "parameters.h" +#include "encoder.h" +#include "vc5_encoder.h" + +#endif // HEADERS_H diff --git a/source/lib/vc5_encoder/parameters.c b/source/lib/vc5_encoder/parameters.c new file mode 100755 index 0000000..2158a71 --- /dev/null +++ b/source/lib/vc5_encoder/parameters.c @@ -0,0 +1,86 @@ +/*! @file parameters.c + * + * @brief Implementation of the data structure used to pass parameters + * to the encoder. + * + * The parameters data structure is currently a simple struct, but + * fields may be added, removed, or replaced. A version number is + * included in the parameters data structure to allow decoders to + * adapt to changes. + + * It is contemplated that future inplementations may use a dictionary + * of key-value pairs which would allow the decoder to determine whether + * a parameter is present. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +//! Current version number of the parameters data structure +#define PARAMETERS_VERSION 1 + +/*! + @brief Initialize the parameters data structure + + The version number of the parameters data structure must be + incremented whenever a change is made to the definition of + the parameters data structure. + + @todo Special initialization required by the metadata? +*/ +CODEC_ERROR InitEncoderParameters(ENCODER_PARAMETERS *parameters) +{ + memset(parameters, 0, sizeof(ENCODER_PARAMETERS)); + parameters->version = PARAMETERS_VERSION; + + // Set the default value for the number of bits per lowpass coefficient + parameters->encoded.lowpass_precision = 16; + + parameters->input.width = 4000; + parameters->input.height = 3000; + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + // The maximum number of bits per component is the internal precision + //parameters->max_bits_per_component = internal_precision; +#endif + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + parameters->enabled_sections = VC5_ENABLED_SECTIONS; +#endif + + // The elementary bitstream is always enabled + parameters->enabled_parts = VC5_ENABLED_PARTS; + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + parameters->layer_count = 1; + parameters->progressive = 1; +#endif + + // Initialize the quantization table using the default values + { + // Initialize to CineForm Filmscan-2 + QUANT quant_table[] = {1, 24, 24, 12, 24, 24, 12, 32, 32, 48}; + memcpy(parameters->quant_table, quant_table, sizeof(parameters->quant_table)); + } + + parameters->verbose_flag = false; + + gpr_rgb_gain_set_defaults(¶meters->rgb_gain); + + parameters->rgb_resolution = VC5_ENCODER_RGB_RESOLUTION_DEFAULT; + + return CODEC_ERROR_OKAY; +} + diff --git a/source/lib/vc5_encoder/parameters.h b/source/lib/vc5_encoder/parameters.h new file mode 100755 index 0000000..b2cac04 --- /dev/null +++ b/source/lib/vc5_encoder/parameters.h @@ -0,0 +1,139 @@ +/*! @file parameters.h + * + * @brief Declare a data structure for holding a table of parameters that is passed + * to the encoder during initialization. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PARAMETERS_H +#define PARAMETERS_H + +#include "vc5_encoder.h" + +/*! + @brief Function prototype for a decompositor + + A decompositor is the opposite of an image composition operator: + It decomposes a frame into one or more frames. + + For example, an interlaced frame can be decomposed into fields or two frames + arranged side-by-side within a single frame can be decomposed into individual + frames. + + Each layer in an encoded sample may correspond to a separate input frame. + For convenience, the reference codec stores the input to the encoder in a + separate file with one frame per file if the encoded sample has a single layer. + To allow the reference encoder to store all of the input frames that are + encoded as separate layers in an encoded sample in a single file, multiple + frames are stored in the file (often using over-under frame packing). The + decomposer unpacks multiple frames in a single frame into individual frames + for encoding with one frame per layer. +*/ + +typedef CODEC_ERROR (* DECOMPOSITOR)(IMAGE *packed_image, IMAGE *image_array[], int frame_count); + +/*! + @brief Declaration of a data structure for passing parameters to the encoder + + The encoded dimensions are the width and height of the planes of pixels as + represented internally in the encoded sample. In the case where the planes + have different dimensions (for example YUV with 4:2:2 sampling), the first + encoded plane (corresponding to the luma plane, for example) is reported. +*/ +typedef struct _encoder_parameters +{ + uint32_t version; //!< Version number for this definition of the parameters + + // BAYER_ORDERING bayer_ordering; + + ENABLED_PARTS enabled_parts; //!< Parts of the VC-5 standard that are enabled + + //! Data structure for the input frame dimensions and format + struct _input_parameters + { + DIMENSION width; //!< Width of the frames input to the encoder + DIMENSION height; //!< Height of the frames input to the encoder + PIXEL_FORMAT format; //!< Pixel format of the input frames + PRECISION precision; //!< Bits per component in the input image + + } input; //!< Dimensions and format of the input frame + + //! Data structure for the encoded representation of the image + struct _encoded_parameters + { +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + DIMENSION width; //!< Width of the encoded frame + DIMENSION height; //!< Height of the encoded frame + IMAGE_FORMAT format; //!< Internal format of the encoded image + PRECISION precision; //!< Encoded precision of the image after scaling +#endif + //! Number of bits used to encode lowpass coefficients + PRECISION lowpass_precision; + + } encoded; //!< Encoded frame dimensions and the encoded format + + //! Array of quantization values indexed by the subband number + QUANT quant_table[MAX_SUBBAND_COUNT]; + +#if VC5_ENABLED_PART(VC5_PART_METADATA) + //! Metadata that controls decoding (currently not used) + METADATA metadata; +#endif +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + int layer_count; + int progressive; + DECOMPOSITOR decompositor; +#endif + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + ENABLED_SECTIONS enabled_sections; +#endif + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + // Definition of the pattern elements + DIMENSION pattern_width; + DIMENSION pattern_height; + DIMENSION components_per_sample; + //PRECISION max_bits_per_component; +#endif + + //! Table for the order in which channels are encoded into the bitstream + CHANNEL channel_order_table[MAX_CHANNEL_COUNT]; + + //! Number of entries in the channel order table (may be less than the channel count) + int channel_order_count; + + //! Flag that controls verbose output + bool verbose_flag; + + gpr_allocator allocator; + + GPR_RGB_RESOLUTION rgb_resolution; + + gpr_rgb_gain rgb_gain; + +} ENCODER_PARAMETERS; + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR InitEncoderParameters(ENCODER_PARAMETERS *parameters); + +#ifdef __cplusplus +} +#endif + +#endif // PARAMETERS_H diff --git a/source/lib/vc5_encoder/raw.c b/source/lib/vc5_encoder/raw.c new file mode 100755 index 0000000..cc1a302 --- /dev/null +++ b/source/lib/vc5_encoder/raw.c @@ -0,0 +1,621 @@ +/*! @file raw.c + * + * @brief Implementation of routines for packing RAW image to a row of pixels. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +#if ENABLED(NEON) +#include +#endif + +/** ------------------- **/ +/** 14 BIT INPUT FORMAT **/ +/** ------------------- **/ + +static void UnpackPixel_14(uint16_t *input_row1_ptr, uint16_t *input_row2_ptr, int column, PIXEL *output_buffer[], bool rggb ) +{ + uint16_t R1, G1, G2, B1; + uint16_t GS, GD, RG, BG; + + uint16_t *GS_output_row_ptr = (uint16_t *)output_buffer[0]; + uint16_t *GD_output_row_ptr = (uint16_t *)output_buffer[3]; + uint16_t *RG_output_row_ptr = (uint16_t *)output_buffer[1]; + uint16_t *BG_output_row_ptr = (uint16_t *)output_buffer[2]; + + const int internal_precision = 12; + const int32_t midpoint = (1 << (internal_precision - 1)); + + if( rggb ) + { + R1 = input_row1_ptr[2 * column + 0]; + G1 = input_row1_ptr[2 * column + 1]; + G2 = input_row2_ptr[2 * column + 0]; + B1 = input_row2_ptr[2 * column + 1]; + } + else + { + G1 = input_row1_ptr[2 * column + 0]; + B1 = input_row1_ptr[2 * column + 1]; + R1 = input_row2_ptr[2 * column + 0]; + G2 = input_row2_ptr[2 * column + 1]; + } + + // Apply protune log curve + R1 = EncoderLogCurve[ R1 >> 2 ]; + G1 = EncoderLogCurve[ G1 >> 2 ]; + G2 = EncoderLogCurve[ G2 >> 2 ]; + B1 = EncoderLogCurve[ B1 >> 2 ]; + + // Difference the green components and subtract green from the red and blue components + GS = (G1 + G2) >> 1; + GD = (G1 - G2 + 2 * midpoint) >> 1; + RG = (R1 - GS + 2 * midpoint) >> 1; + BG = (B1 - GS + 2 * midpoint) >> 1; + + GS_output_row_ptr[column] = clamp_uint(GS, internal_precision); + GD_output_row_ptr[column] = clamp_uint(GD, internal_precision); + RG_output_row_ptr[column] = clamp_uint(RG, internal_precision); + BG_output_row_ptr[column] = clamp_uint(BG, internal_precision); +} + +#if ENABLED(NEON) + +#define UnpackPixel_14_8x UnpackPixel_14_8x_NEON_ +static void UnpackPixel_14_8x_NEON_(uint16_t *input_row1_ptr, uint16_t *input_row2_ptr, int column, PIXEL *output_buffer[], bool rggb ) +{ + int i; + uint16x8x2_t row_1, row_2; + + const int internal_precision = 12; + const int32_t midpoint = (1 << (internal_precision - 1)); + + // Apply protune log curve + { + uint16_t input_row1_12b[16]; + uint16_t input_row2_12b[16]; + + for (i = 0; i < 16; ++i) + { + input_row1_12b[i] = EncoderLogCurve[ input_row1_ptr[2 * column + i] >> 2 ]; + input_row2_12b[i] = EncoderLogCurve[ input_row2_ptr[2 * column + i] >> 2 ]; + } + + row_1 = vld2q_u16( input_row1_12b ); + row_2 = vld2q_u16( input_row2_12b ); + } + + int16x8_t R1, G1, G2, B1; + + if( rggb ) + { + R1 = vreinterpretq_s16_u16( row_1.val[0] ); + G1 = vreinterpretq_s16_u16( row_1.val[1] ); + G2 = vreinterpretq_s16_u16( row_2.val[0] ); + B1 = vreinterpretq_s16_u16( row_2.val[1] ); + } + else + { + G1 = vreinterpretq_s16_u16( row_1.val[0] ); + B1 = vreinterpretq_s16_u16( row_1.val[1] ); + R1 = vreinterpretq_s16_u16( row_2.val[0] ); + G2 = vreinterpretq_s16_u16( row_2.val[1] ); + } + + int16x8_t GS, GD, RG, BG; + + GS = vhaddq_s16(G1, G2); + vst1q_s16( output_buffer[0] + column, GS ); + + { + const int16x8_t __midpoint_x2 = vdupq_n_s16(midpoint * 2); + + GD = vsubq_s16(G1, G2); + GD = vhaddq_s16(GD, __midpoint_x2); + vst1q_s16( output_buffer[3] + column, GD ); + + GS = vsubq_s16( __midpoint_x2, GS ); + } + + RG = vhaddq_s16(R1, GS); + vst1q_s16( output_buffer[1] + column, RG ); + + BG = vhaddq_s16(B1, GS); + vst1q_s16( output_buffer[2] + column, BG ); +} + +#else + +#define UnpackPixel_14_8x UnpackPixel_14_8x_C_ +static void UnpackPixel_14_8x_C_(uint16_t *input_row1_ptr, uint16_t *input_row2_ptr, int column, PIXEL *output_buffer[], bool rggb ) +{ + int i; + for ( i = 0; i < 8; i++) + { + UnpackPixel_14(input_row1_ptr, input_row2_ptr, column + i, output_buffer, rggb ); + } +} + +#endif + +void UnpackImage_14(const PACKED_IMAGE *input, UNPACKED_IMAGE *output, ENABLED_PARTS enabled_parts, bool rggb ) +{ + uint8_t *input_buffer = (uint8_t *)input->buffer + input->offset; + + const DIMENSION input_width = input->width / 2; + const DIMENSION input_width_m8 = (input_width / 8) * 8; + + const DIMENSION input_height = input->height / 2; + + size_t input_pitch = input->pitch; + + PIXEL *output_row_ptr_array[MAX_CHANNEL_COUNT]; + uint32_t output_row_ptr_array_pitch[MAX_CHANNEL_COUNT]; + + uint16_t *input_row_ptr = (uint16_t*)input_buffer; + + int channel_number; + + int row; + + for (channel_number = 0; channel_number < MAX_CHANNEL_COUNT; channel_number++) + { + output_row_ptr_array[channel_number] = (PIXEL *)(output->component_array_list[channel_number].data); + + // output->component_array_list[channel_number].pitch is pitch in bytes, so we need to convert it to pitch in PIXELS + output_row_ptr_array_pitch[channel_number] = (output->component_array_list[channel_number].pitch / sizeof(PIXEL)); + } + + for (row = 0; row < input_height; row++) + { + uint16_t* input_row2_ptr = input_row_ptr + (input_pitch / sizeof(uint16_t)); + + int column = 0; + + // Unpack the row of Bayer components from the BYR4 pattern elements + for (; column < input_width_m8; column+= 8) + { + UnpackPixel_14_8x(input_row_ptr, input_row2_ptr, column, output_row_ptr_array, rggb ); + } + + // Unpack the row of Bayer components from the BYR4 pattern elements + for (; column < input_width; column++) + { + UnpackPixel_14(input_row_ptr, input_row2_ptr, column, output_row_ptr_array, rggb ); + } + + input_row_ptr += input_pitch; + + for (channel_number = 0; channel_number < MAX_CHANNEL_COUNT; channel_number++) + { + output_row_ptr_array[channel_number] += output_row_ptr_array_pitch[channel_number]; + } + } +} + +/** ------------------- **/ +/** 12 BIT INPUT FORMAT **/ +/** ------------------- **/ + +static void UnpackPixel_12(uint16_t *input_row1_ptr, uint16_t *input_row2_ptr, int column, PIXEL *output_buffer[], bool rggb ) +{ + uint16_t R1, G1, G2, B1; + uint16_t GS, GD, RG, BG; + + uint16_t *GS_output_row_ptr = (uint16_t *)output_buffer[0]; + uint16_t *GD_output_row_ptr = (uint16_t *)output_buffer[3]; + uint16_t *RG_output_row_ptr = (uint16_t *)output_buffer[1]; + uint16_t *BG_output_row_ptr = (uint16_t *)output_buffer[2]; + + const int internal_precision = 12; + const int32_t midpoint = (1 << (internal_precision - 1)); + + if( rggb ) + { + R1 = input_row1_ptr[2 * column + 0]; + G1 = input_row1_ptr[2 * column + 1]; + G2 = input_row2_ptr[2 * column + 0]; + B1 = input_row2_ptr[2 * column + 1]; + } + else + { + G1 = input_row1_ptr[2 * column + 0]; + B1 = input_row1_ptr[2 * column + 1]; + R1 = input_row2_ptr[2 * column + 0]; + G2 = input_row2_ptr[2 * column + 1]; + } + + // Apply protune log curve + R1 = EncoderLogCurve[ R1 ]; + G1 = EncoderLogCurve[ G1 ]; + G2 = EncoderLogCurve[ G2 ]; + B1 = EncoderLogCurve[ B1 ]; + + // Difference the green components and subtract green from the red and blue components + GS = (G1 + G2) >> 1; + GD = (G1 - G2 + 2 * midpoint) >> 1; + RG = (R1 - GS + 2 * midpoint) >> 1; + BG = (B1 - GS + 2 * midpoint) >> 1; + + GS_output_row_ptr[column] = clamp_uint(GS, internal_precision); + GD_output_row_ptr[column] = clamp_uint(GD, internal_precision); + RG_output_row_ptr[column] = clamp_uint(RG, internal_precision); + BG_output_row_ptr[column] = clamp_uint(BG, internal_precision); +} + +#if ENABLED(NEON) + +#define UnpackPixel_12_8x UnpackPixel_12_8x_NEON_ +static void UnpackPixel_12_8x_NEON_(uint16_t *input_row1_ptr, uint16_t *input_row2_ptr, int column, PIXEL *output_buffer[], bool rggb ) +{ + int i; + uint16x8x2_t row_1, row_2; + + const int internal_precision = 12; + const int32_t midpoint = (1 << (internal_precision - 1)); + + // Apply protune log curve + { + uint16_t input_row1_12b[16]; + uint16_t input_row2_12b[16]; + + for (i = 0; i < 16; ++i) + { + input_row1_12b[i] = EncoderLogCurve[ input_row1_ptr[2 * column + i] ]; + input_row2_12b[i] = EncoderLogCurve[ input_row2_ptr[2 * column + i] ]; + } + + row_1 = vld2q_u16( input_row1_12b ); + row_2 = vld2q_u16( input_row2_12b ); + } + + int16x8_t R1, G1, G2, B1; + + if( rggb ) + { + R1 = vreinterpretq_s16_u16( row_1.val[0] ); + G1 = vreinterpretq_s16_u16( row_1.val[1] ); + G2 = vreinterpretq_s16_u16( row_2.val[0] ); + B1 = vreinterpretq_s16_u16( row_2.val[1] ); + } + else + { + G1 = vreinterpretq_s16_u16( row_1.val[0] ); + B1 = vreinterpretq_s16_u16( row_1.val[1] ); + R1 = vreinterpretq_s16_u16( row_2.val[0] ); + G2 = vreinterpretq_s16_u16( row_2.val[1] ); + } + + int16x8_t GS, GD, RG, BG; + + GS = vhaddq_s16(G1, G2); + vst1q_s16( output_buffer[0] + column, GS ); + + { + const int16x8_t __midpoint_x2 = vdupq_n_s16(midpoint * 2); + + GD = vsubq_s16(G1, G2); + GD = vhaddq_s16(GD, __midpoint_x2); + vst1q_s16( output_buffer[3] + column, GD ); + + GS = vsubq_s16( __midpoint_x2, GS ); + } + + RG = vhaddq_s16(R1, GS); + vst1q_s16( output_buffer[1] + column, RG ); + + BG = vhaddq_s16(B1, GS); + vst1q_s16( output_buffer[2] + column, BG ); +} + +#else + +#define UnpackPixel_12_8x UnpackPixel_12_8x_C_ +static void UnpackPixel_12_8x_C_(uint16_t *input_row1_ptr, uint16_t *input_row2_ptr, int column, PIXEL *output_buffer[], bool rggb ) +{ + int i; + for ( i = 0; i < 8; i++) + { + UnpackPixel_12(input_row1_ptr, input_row2_ptr, column + i, output_buffer, rggb ); + } +} + +#endif + +void UnpackImage_12(const PACKED_IMAGE *input, UNPACKED_IMAGE *output, ENABLED_PARTS enabled_parts, bool rggb ) +{ + uint8_t *input_buffer = (uint8_t *)input->buffer + input->offset; + + const DIMENSION input_width = input->width / 2; + const DIMENSION input_width_m8 = (input_width / 8) * 8; + const DIMENSION input_height = input->height / 2; + + size_t input_pitch = input->pitch; + + PIXEL *output_row_ptr_array[MAX_CHANNEL_COUNT]; + uint32_t output_row_ptr_array_pitch[MAX_CHANNEL_COUNT]; + + uint16_t *input_row_ptr = (uint16_t*)input_buffer; + + int channel_number; + + int row; + + for (channel_number = 0; channel_number < MAX_CHANNEL_COUNT; channel_number++) + { + output_row_ptr_array[channel_number] = (PIXEL *)(output->component_array_list[channel_number].data); + + // output->component_array_list[channel_number].pitch is pitch in bytes, so we need to convert it to pitch in PIXELS + output_row_ptr_array_pitch[channel_number] = (output->component_array_list[channel_number].pitch / sizeof(PIXEL)); + } + + for (row = 0; row < input_height; row++) + { + uint16_t* input_row2_ptr = input_row_ptr + (input_pitch / sizeof(uint16_t)); + + int column = 0; + + // Unpack the row of Bayer components from the BYR4 pattern elements + for (; column < input_width_m8; column+= 8) + { + UnpackPixel_12_8x(input_row_ptr, input_row2_ptr, column, output_row_ptr_array, rggb ); + } + + // Unpack the row of Bayer components from the BYR4 pattern elements + for (; column < input_width; column++) + { + UnpackPixel_12(input_row_ptr, input_row2_ptr, column, output_row_ptr_array, rggb ); + } + + input_row_ptr += input_pitch; + + for (channel_number = 0; channel_number < MAX_CHANNEL_COUNT; channel_number++) + { + output_row_ptr_array[channel_number] += output_row_ptr_array_pitch[channel_number]; + } + } +} + +/** -------------------------- **/ +/** 12 bit PACKED INPUT FORMAT **/ +/** -------------------------- **/ + +static void UnpackPixel_12P(uint16_t *input_row1_ptr, uint16_t *input_row2_ptr, int column, PIXEL *output_buffer[], bool rggb ) +{ + uint16_t R1, G1, G2, B1; + uint16_t GS, GD, RG, BG; + + const int internal_precision = 12; + const int32_t midpoint = (1 << (internal_precision - 1)); + + const unsigned int byte_offset = (column * 3); + + { // read first row data + uint8_t* row1_ptr = (uint8_t*)input_row1_ptr; + + unsigned char byte_0 = row1_ptr[byte_offset + 0]; + unsigned char byte_1 = row1_ptr[byte_offset + 1]; + unsigned char byte_2 = row1_ptr[byte_offset + 2]; + + if( rggb ) + { + R1 = (byte_0) + ((byte_1 & 0x0f) << 8); + G1 = (byte_2 << 4) + ((byte_1 & 0xf0) >> 4); + } + else + { + G1 = (byte_0) + ((byte_1 & 0x0f) << 8); + B1 = (byte_2 << 4) + ((byte_1 & 0xf0) >> 4); + } + } + + { // read second row data + uint8_t* row2_ptr = (uint8_t*)input_row2_ptr; + + unsigned char byte_0 = row2_ptr[byte_offset + 0]; + unsigned char byte_1 = row2_ptr[byte_offset + 1]; + unsigned char byte_2 = row2_ptr[byte_offset + 2]; + + if( rggb ) + { + G2 = (byte_0) + ((byte_1 & 0x0f) << 8); + B1 = (byte_2 << 4) + ((byte_1 & 0xf0) >> 4); + } + else + { + R1 = (byte_0) + ((byte_1 & 0x0f) << 8); + G2 = (byte_2 << 4) + ((byte_1 & 0xf0) >> 4); + } + } + + // Apply protune log curve + G1 = EncoderLogCurve[ G1 ]; + B1 = EncoderLogCurve[ B1 ]; + R1 = EncoderLogCurve[ R1 ]; + G2 = EncoderLogCurve[ G2 ]; + + // difference the green components and subtract green from the red and blue components + GS = (G1 + G2) >> 1; + GD = (G1 - G2 + 2 * midpoint) >> 1; + RG = (R1 - GS + 2 * midpoint) >> 1; + BG = (B1 - GS + 2 * midpoint) >> 1; + + { // write output + uint16_t *GS_output_row_ptr = (uint16_t *)output_buffer[0]; + uint16_t *GD_output_row_ptr = (uint16_t *)output_buffer[3]; + uint16_t *RG_output_row_ptr = (uint16_t *)output_buffer[1]; + uint16_t *BG_output_row_ptr = (uint16_t *)output_buffer[2]; + + GS_output_row_ptr[column] = clamp_uint(GS, internal_precision); + GD_output_row_ptr[column] = clamp_uint(GD, internal_precision); + RG_output_row_ptr[column] = clamp_uint(RG, internal_precision); + BG_output_row_ptr[column] = clamp_uint(BG, internal_precision); + } +} + +#if ENABLED(NEON) + +#define UnpackPixel_12P_8x UnpackPixel_12P_8x_NEON_ +static void UnpackPixel_12P_8x_NEON_(uint16_t *input_row1_ptr, uint16_t *input_row2_ptr, int column, PIXEL *output_buffer[], bool rggb ) +{ + int i; + uint16x8_t g1, b1, r1, g2; + + const int internal_precision = 12; + const int32_t midpoint = (1 << (internal_precision - 1)); + const unsigned int byte_offset = (column * 3)/2; + + // Apply protune log curve + { + uint8_t* row1_ptr = (uint8_t*) &input_row1_ptr[byte_offset]; //Taken care: input_row1_ptr is 16-bit pointer. So halving it + uint8x8x3_t __byte012 = vld3_u8(row1_ptr); //Make sure you move only 24 bytes at a time + uint8x8_t __byte0 = __byte012.val[0]; + uint8x8_t __byte1 = __byte012.val[1]; + uint8x8_t __byte2 = __byte012.val[2]; + + if( rggb ) + { + r1 = vaddw_u8(vshll_n_u8(vshl_n_u8(__byte1, 4), 4), __byte0); + g1 = vaddw_u8(vshll_n_u8(__byte2, 4), vshr_n_u8(__byte1, 4)); + } + else + { + g1 = vaddw_u8(vshll_n_u8(vshl_n_u8(__byte1, 4), 4), __byte0); + b1 = vaddw_u8(vshll_n_u8(__byte2, 4), vshr_n_u8(__byte1, 4)); + } + } + { + uint8_t* row2_ptr = (uint8_t*) &input_row2_ptr[byte_offset]; + uint8x8x3_t __byte012 = vld3_u8(row2_ptr); + uint8x8_t __byte0 = __byte012.val[0]; + uint8x8_t __byte1 = __byte012.val[1]; + uint8x8_t __byte2 = __byte012.val[2]; + + if( rggb ) + { + g2 = vaddw_u8(vshll_n_u8(vshl_n_u8(__byte1, 4), 4), __byte0); + b1 = vaddw_u8(vshll_n_u8(__byte2, 4), vshr_n_u8(__byte1, 4)); + } + else + { + r1 = vaddw_u8(vshll_n_u8(vshl_n_u8(__byte1, 4), 4), __byte0); + g2 = vaddw_u8(vshll_n_u8(__byte2, 4), vshr_n_u8(__byte1, 4)); + } + } + + for(i = 0; i < 8; i++) + { + g1[i] = EncoderLogCurve[g1[i]]; + b1[i] = EncoderLogCurve[b1[i]]; + r1[i] = EncoderLogCurve[r1[i]]; + g2[i] = EncoderLogCurve[g2[i]]; + } + + int16x8_t R1, G1, G2, B1; + + G1 = vreinterpretq_s16_u16( g1 ); + B1 = vreinterpretq_s16_u16( b1 ); + R1 = vreinterpretq_s16_u16( r1 ); + G2 = vreinterpretq_s16_u16( g2 ); + + int16x8_t GS, GD, RG, BG; + + GS = vhaddq_s16(G1, G2); + vst1q_s16( output_buffer[0] + column, GS ); + + { + const int16x8_t __midpoint_x2 = vdupq_n_s16(midpoint * 2); + + GD = vsubq_s16(G1, G2); + GD = vhaddq_s16(GD, __midpoint_x2); + vst1q_s16( output_buffer[3] + column, GD ); + GS = vsubq_s16( __midpoint_x2, GS ); + } + + RG = vhaddq_s16(R1, GS); + vst1q_s16( output_buffer[1] + column, RG ); + + BG = vhaddq_s16(B1, GS); + vst1q_s16( output_buffer[2] + column, BG ); +} + +#else + +#define UnpackPixel_12P_8x UnpackPixel_12P_8x_C_ +static void UnpackPixel_12P_8x_C_(uint16_t *input_row1_ptr, uint16_t *input_row2_ptr, int column, PIXEL *output_buffer[], bool rggb ) +{ + int i; + for ( i = 0; i < 8; i++) + { + UnpackPixel_12P(input_row1_ptr, input_row2_ptr, column + i, output_buffer, rggb ); + } +} + +#endif + +void UnpackImage_12P(const PACKED_IMAGE *input, UNPACKED_IMAGE *output, ENABLED_PARTS enabled_parts, bool rggb ) +{ + uint8_t *input_buffer = (uint8_t *)input->buffer + input->offset; + + const DIMENSION input_width = input->width / 2; + const DIMENSION input_width_m8 = (input_width / 8) * 8; + const DIMENSION input_height = input->height / 2; + + size_t input_pitch = input->pitch; + + PIXEL *output_row_ptr_array[MAX_CHANNEL_COUNT]; + uint32_t output_row_ptr_array_pitch[MAX_CHANNEL_COUNT]; + + uint16_t *input_row_ptr = (uint16_t*)input_buffer; + + int channel_number; + + int row; + + for (channel_number = 0; channel_number < MAX_CHANNEL_COUNT; channel_number++) + { + output_row_ptr_array[channel_number] = (PIXEL *)(output->component_array_list[channel_number].data); + + output_row_ptr_array_pitch[channel_number] = (output->component_array_list[channel_number].pitch / sizeof(PIXEL)); + } + + for (row = 0; row < input_height; row++) + { + uint16_t* input_row2_ptr = input_row_ptr + (input_pitch / sizeof(uint16_t)); + + int column = 0; + + // Unpack the row of Bayer components from the BYR4 pattern elements + for (; column < input_width_m8; column+= 8) + { + UnpackPixel_12P_8x(input_row_ptr, input_row2_ptr, column, output_row_ptr_array, rggb ); + } + + // Unpack the row of Bayer components from the BYR4 pattern elements + for (; column < input_width; column++) + { + UnpackPixel_12P(input_row_ptr, input_row2_ptr, column, output_row_ptr_array, rggb ); + } + + input_row_ptr += input_pitch; + + for (channel_number = 0; channel_number < MAX_CHANNEL_COUNT; channel_number++) + { + output_row_ptr_array[channel_number] += output_row_ptr_array_pitch[channel_number]; + } + } +} + diff --git a/source/lib/vc5_encoder/raw.h b/source/lib/vc5_encoder/raw.h new file mode 100755 index 0000000..894d60e --- /dev/null +++ b/source/lib/vc5_encoder/raw.h @@ -0,0 +1,36 @@ +/*! @file raw.h + * + * @brief Declaration of routines for packing RAW image to a row of pixels. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef RAW_H +#define RAW_H + +#ifdef __cplusplus +extern "C" { +#endif + + void UnpackImage_14(const PACKED_IMAGE *input, UNPACKED_IMAGE *output, ENABLED_PARTS enabled_parts, bool rggb ); + + void UnpackImage_12(const PACKED_IMAGE *input, UNPACKED_IMAGE *output, ENABLED_PARTS enabled_parts, bool rggb ); + + void UnpackImage_12P(const PACKED_IMAGE *input, UNPACKED_IMAGE *output, ENABLED_PARTS enabled_parts, bool rggb ); + +#ifdef __cplusplus +} +#endif + +#endif // RAW_H diff --git a/source/lib/vc5_encoder/sections.c b/source/lib/vc5_encoder/sections.c new file mode 100755 index 0000000..47efc0d --- /dev/null +++ b/source/lib/vc5_encoder/sections.c @@ -0,0 +1,293 @@ +/*! @file sections.c + * + * @brief Implementation of code for encoding sections + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + +/* + @brief Write codec state parameters used for decoding the section into the bitstream + + A section element may be decoded independently from other sections of the same type. + Concurrent decoding implies that all codec state parameters needed to decode a section + element be present in that section element. + + In principle, it is only necessary to write the codec state parameters that may be changed + as other section elements are decoded independently. This sample encoder takes the simple + approach and writes all non-header codec state parameters into the bitstream. + */ +static CODEC_ERROR PutCodecState(ENCODER *encoder, BITSTREAM *stream, SECTION_NUMBER section_number) +{ + CODEC_STATE *codec = &encoder->codec; + TAGWORD prescale_shift = 0; + + switch (section_number) + { + case SECTION_NUMBER_IMAGE: + assert(0); + break; + + case SECTION_NUMBER_HEADER: + // No codec state parameters to be written into the bitstream + break; + + case SECTION_NUMBER_CHANNEL: + // Encode the transform prescale for the first channel (assume all channels are the same) + prescale_shift = PackTransformPrescale(&encoder->transform[0]); + + PutTagPair(stream, CODEC_TAG_ChannelNumber, codec->channel_number); + PutTagPair(stream, CODEC_TAG_SubbandNumber, codec->subband_number); + PutTagPair(stream, CODEC_TAG_LowpassPrecision, codec->lowpass_precision); + PutTagPair(stream, CODEC_TAG_Quantization, codec->band.quantization); + PutTagPair(stream, CODEC_TAG_PrescaleShift, prescale_shift); + +#if VC5_ENABLED_PART(VC5_PART_IMAGE_FORMATS) + if (!IsPartEnabled(encoder->enabled_parts, VC5_PART_IMAGE_FORMATS)) + { + PutTagPair(stream, CODEC_TAG_ChannelWidth, codec->channel_width); + PutTagPair(stream, CODEC_TAG_ChannelHeight, codec->channel_height); + } +#endif +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + if (IsPartEnabled(encoder->enabled_parts, VC5_PART_LAYERS)) + { + PutTagPair(stream, CODEC_TAG_LayerNumber, codec->layer_number); + } +#endif + break; + + case SECTION_NUMBER_WAVELET: + PutTagPair(stream, CODEC_TAG_ChannelNumber, codec->channel_number); + PutTagPair(stream, CODEC_TAG_SubbandNumber, codec->subband_number); + PutTagPair(stream, CODEC_TAG_LowpassPrecision, codec->lowpass_precision); + //PutTagPair(stream, CODEC_TAG_Quantization, codec->band.quantization); + //PutTagPair(stream, CODEC_TAG_PrescaleShift, prescale_shift); + break; + + case SECTION_NUMBER_SUBBAND: + PutTagPair(stream, CODEC_TAG_ChannelNumber, codec->channel_number); + PutTagPair(stream, CODEC_TAG_SubbandNumber, codec->subband_number); + PutTagPair(stream, CODEC_TAG_LowpassPrecision, codec->lowpass_precision); + PutTagPair(stream, CODEC_TAG_Quantization, codec->band.quantization); + //PutTagPair(stream, CODEC_TAG_PrescaleShift, prescale_shift); + break; + + default: + assert(0); + return CODEC_ERROR_UNEXPECTED; + } + + return CODEC_ERROR_OKAY; +} + +/* + @brief Return true if specified type of section is enabled + */ +bool IsSectionEnabled(ENCODER *encoder, SECTION_NUMBER section_number) +{ + if (IsPartEnabled(encoder->enabled_parts, VC5_PART_SECTIONS)) + { + if (SECTION_NUMBER_MINIMUM <= section_number && section_number <= SECTION_NUMBER_MAXIMUM) + { + uint32_t section_mask = SECTION_NUMBER_MASK(section_number); + + if (encoder->enabled_sections & section_mask) { + return true; + } + } + } + + // None of the predefined VC-5 sections are enabled + return false; +} + +/* + @brief Start a new section with the specified tag + + The location of the the tag-value pair that marks the beginning of the new + section is pushed onto a stack so that the tag-value pair can be updated with + the actual size of the section when the section is ended by a call to the + @ref EndSection function. + */ +CODEC_ERROR BeginSection(BITSTREAM *bitstream, TAGWORD tag) +{ + return PushSampleSize(bitstream, tag); +} + +/* + @brief End a section + + Update the tag-value pair that marks the section with the actual size of the section. + */ +CODEC_ERROR EndSection(BITSTREAM *bitstream) +{ + return PopSampleSize(bitstream); +} + +/*! + @brief Write an image section header into the bitstream + */ +CODEC_ERROR BeginImageSection(struct _encoder *encoder, BITSTREAM *stream) +{ + assert(0); + return CODEC_ERROR_OKAY; +} + +/* + @brief Write a section header for the bitstream header into the bitstream + */ +CODEC_ERROR BeginHeaderSection(struct _encoder *encoder, BITSTREAM *stream) +{ + // Write the section header for the bitstream header into the bitstream + return BeginSection(stream, CODEC_TAG_HeaderSectionTag); +} + +/* + @brief Write a layer section header into the bitstream + + Any codec state parameters that are required to decode the layer must be explicitly + written into the bitstream so that the layer sections and be decoded concurrently. + */ +CODEC_ERROR BeginLayerSection(struct _encoder *encoder, BITSTREAM *stream) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Duplicate all codec state parameters required for decoding the layer + PutCodecState(encoder, stream, SECTION_NUMBER_LAYER); + + // Write the section header for the layer into the bitstream + error = BeginSection(stream, CODEC_TAG_LayerSectionTag); + + return error; +} + +/* + @brief Write a channel section header into the bitstream + + Any codec state parameters that are required to decode the channel must be explicitly + written into the bitstream so that the channel sections and be decoded concurrently. + */ +CODEC_ERROR BeginChannelSection(ENCODER *encoder, BITSTREAM *stream) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Duplicate all codec state parameters required for decoding the channel + PutCodecState(encoder, stream, SECTION_NUMBER_CHANNEL); + + // Write the section header for the channel into the bitstream + error = BeginSection(stream, CODEC_TAG_ChannelSectionTag); + + return error; +} + +/* + @brief Write a wavelet section header into the bitstream + + Any codec state parameters that are required to decode the wavelet must be explicitly + written into the bitstream so that the wavelet sections and be decoded concurrently. + */ +CODEC_ERROR BeginWaveletSection(struct _encoder *encoder, BITSTREAM *stream) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Duplicate all codec state parameters required for decoding the wavelet + PutCodecState(encoder, stream, SECTION_NUMBER_WAVELET); + + // Write the section header for the wavelet into the bitstream + error = BeginSection(stream, CODEC_TAG_WaveletSectionTag); + + return error; +} + +/* + @brief Write a subband section header into the bitstream + + Any codec state parameters that are required to decode the subband must be explicitly + written into the bitstream so that the subband sections and be decoded concurrently. + */ +CODEC_ERROR BeginSubbandSection(struct _encoder *encoder, BITSTREAM *stream) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + + // Duplicate all codec state parameters required for decoding the subband + PutCodecState(encoder, stream, SECTION_NUMBER_SUBBAND); + + // Write the section header for the subband into the bitstream + error = BeginSection(stream, CODEC_TAG_SubbandSectionTag); + + return error; +} + +/*! + @brief Set the flags that indicate which sections in VC-5 Part 6 are enabled + + The argument is a list of comma-separated integers for the section numbers + in the VC-5 Part 2 conformance specification that are enabled for this invocation + of the encoder. + + Note: Enabling sections at runtime has no effect unless support for sections + is compiled into the program by enabling the corresponding compile-time switch + for VC-5 part 6 (sections). + */ +bool GetEnabledSections(const char *string, uint32_t *enabled_sections_out) +{ + if (string != NULL && enabled_sections_out != NULL) + { + // No sections are enabled by default + ENABLED_SECTIONS enabled_sections = 0; + + const char *p = string; + assert(p != NULL); + while (*p != '\0') + { + char *q = NULL; + long section_number; + if (!isdigit((int)*p)) break; + section_number = strtol(p, &q, 10); + + // Is the section number in bounds? + if (SECTION_NUMBER_MINIMUM <= section_number && section_number <= SECTION_NUMBER_MAXIMUM) + { + // Set the bit that corresponds to this section number + enabled_sections |= SECTION_NUMBER_MASK(section_number); + + // Advance to the next section number in the command-line argument + p = (*q != '\0') ? q + 1 : q; + } + else + { + // Invalid section number + assert(0); + return false; + } + } + + // Return the bit mask for the enabled sections + *enabled_sections_out = enabled_sections; + + // Should have parsed all section numbers in the argument string + assert(*p == '\0'); + return true; + } + + // Invalid input arguments + return false; +} + + +#endif diff --git a/source/lib/vc5_encoder/sections.h b/source/lib/vc5_encoder/sections.h new file mode 100755 index 0000000..602dd81 --- /dev/null +++ b/source/lib/vc5_encoder/sections.h @@ -0,0 +1,91 @@ +/*! @file sections.h + * + * @brief Declaration of routines for handling sections in the encoder. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef SECTIONS_H +#define SECTIONS_H + +/* + @brief Enumeration of the predefined section numbers + + The predefined section numbers are defined in ST 2073-2. + */ +typedef enum _section_number +{ + SECTION_NUMBER_IMAGE = 1, //!< Image section + SECTION_NUMBER_HEADER = 2, //!< Bitstream header section + SECTION_NUMBER_LAYER = 3, //!< Layer section + SECTION_NUMBER_CHANNEL = 4, //!< Channel section + SECTION_NUMBER_WAVELET = 5, //!< Wavelet section + SECTION_NUMBER_SUBBAND = 6, //!< Subband section + + //TODO: Add more section number definitions as required + + //! Modify the smallest and largest section numbers as more sections are added + SECTION_NUMBER_MINIMUM = SECTION_NUMBER_IMAGE, + SECTION_NUMBER_MAXIMUM = SECTION_NUMBER_SUBBAND, + +} SECTION_NUMBER; + +/* + @Macro for creating a section number bit mask from a section number + + The macro does not check that the section number argument is valid. + */ +#define SECTION_NUMBER_MASK(section_number) (1 << (section_number - 1)) + +/* + @brief Data type for the bit mask that represents enabled sections + + The bit mask indicates which section numbers defined in ST 2073-2 are enabled + at runtime. + */ +typedef uint32_t ENABLED_SECTIONS; + +#define VC5_ENABLED_SECTIONS (SECTION_NUMBER_MASK(SECTION_NUMBER_CHANNEL) | \ + SECTION_NUMBER_MASK(SECTION_NUMBER_WAVELET) | \ + SECTION_NUMBER_MASK(SECTION_NUMBER_SUBBAND)) + +#ifdef __cplusplus +extern "C" { +#endif + + bool IsSectionEnabled(struct _encoder *encoder, SECTION_NUMBER section_number); + + CODEC_ERROR BeginSection(BITSTREAM *bitstream, TAGWORD tag); + + CODEC_ERROR EndSection(BITSTREAM *bitstream); + + CODEC_ERROR BeginImageSection(struct _encoder *encoder, BITSTREAM *stream); + + CODEC_ERROR BeginHeaderSection(struct _encoder *encoder, BITSTREAM *stream); + + CODEC_ERROR BeginLayerSection(struct _encoder *encoder, BITSTREAM *stream); + + CODEC_ERROR BeginChannelSection(struct _encoder *encoder, BITSTREAM *stream); + + CODEC_ERROR BeginWaveletSection(struct _encoder *encoder, BITSTREAM *stream); + + CODEC_ERROR BeginSubbandSection(struct _encoder *encoder, BITSTREAM *stream); + + bool GetEnabledSections(const char *string, uint32_t *enabled_sections_out); + +#ifdef __cplusplus +} +#endif + +#endif // SECTIONS_H diff --git a/source/lib/vc5_encoder/syntax.c b/source/lib/vc5_encoder/syntax.c new file mode 100755 index 0000000..14afdf1 --- /dev/null +++ b/source/lib/vc5_encoder/syntax.c @@ -0,0 +1,276 @@ +/*! @file syntax.c + * + * @brief Implementation of functions for writing bitstream syntax of encoded samples. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + +/*! + @brief Pop the top value from the sample offset stack + */ +static uint32_t PopSampleOffsetStack(BITSTREAM *bitstream) +{ + assert(bitstream->sample_offset_count > 0); + return bitstream->sample_offset_stack[--bitstream->sample_offset_count]; +} + +#endif + +/*! + @brief Write a segment at the specified offset in the bitstream + + The segment at the specified offset in the bitstream is overwritten by the new + segment provided as an argument. Typically this is done to update a segment that + is intended to provide the size or offset to a syntax element in the encoded sample. + */ +CODEC_ERROR PutSampleOffsetSegment(BITSTREAM *bitstream, uint32_t offset, TAGVALUE segment) +{ + // Translate the segment to network byte order + uint32_t buffer = Swap32(segment.longword); + + // Must write the segment on a segment boundary + assert((offset % sizeof(TAGVALUE)) == 0); + + // Write the segment to the byte stream at the specified offset + return PutBlock(bitstream->stream, &buffer, sizeof(buffer), offset); +} + +static bool IsTagOptional(TAGWORD tag) +{ + return (tag < 0); +} + +/*! + @brief Write the trailer for the lowpass band into the bitstream + + This routine writes a marker into the bitstream that can aid in debugging, + but the most important function is to update the segment that contains the + size of this subband with the actual size of the lowpass band. + */ +CODEC_ERROR PutVideoLowpassTrailer(BITSTREAM *stream) +{ + // Check that the bitstream is tag aligned before writing the pixels + assert(IsAlignedSegment(stream)); + + // Set the size of the large chunk for the lowpass band codeblock + PopSampleSize(stream); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write the next tag value pair to the bitstream + + @todo Change the code to use the @ref PutLong function? + */ +CODEC_ERROR PutTagValue(BITSTREAM *stream, TAGVALUE segment) +{ + CODEC_ERROR error; + + // Write the tag to the bitstream + error = PutBits(stream, segment.tuple.tag, tagword_count); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Write the value to the bitstream + error = PutBits(stream, segment.tuple.value, tagword_count); + + return error; +} + +/*! + @brief Write a required tag value pair + + @todo Should the tag value pair be output on a segment boundary? + */ +CODEC_ERROR PutTagPair(BITSTREAM *stream, int tag, int value) +{ + // The bitstream should be aligned on a tag word boundary + assert(IsAlignedTag(stream)); + + // The value must fit within a tag word + assert(((uint32_t)value & ~(uint32_t)CODEC_TAG_MASK) == 0); + + PutLong(stream, ((uint32_t )tag << 16) | (value & CODEC_TAG_MASK)); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write an optional tag value pair + + @todo Should the tag value pair be output on a segment boundary? + */ +CODEC_ERROR PutTagPairOptional(BITSTREAM *stream, int tag, int value) +{ + // The bitstream should be aligned on a tag word boundary + assert(IsAlignedTag(stream)); + + // The value must fit within a tag word + assert(((uint32_t)value & ~(uint32_t)CODEC_TAG_MASK) == 0); + + // Set the optional tag bit + //tag |= CODEC_TAG_OPTIONAL; + //tag = NEG(tag); + tag = neg(tag); + + PutLong(stream, ((uint32_t )tag << 16) | (value & CODEC_TAG_MASK)); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Write a tag value pair that specifies the size of a syntax element + + The routine pushes the current position in the bitstream onto the sample offset + stack and writes a tag value pair for the size of the current syntax element. + The routine @ref PopSampleSize overwrites the segment with a tag value pair + that contains the actual size of the syntax element. + + This routine corresponds to the routine SizeTagPush in the current codec implementation. + */ +CODEC_ERROR PushSampleSize(BITSTREAM *bitstream, TAGWORD tag) +{ +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + + size_t position = GetBitstreamPosition(bitstream); + + // Check for stack overflow + assert(bitstream->sample_offset_count < MAX_SAMPLE_OFFSET_COUNT); + + // Check that the bitstream position can be pushed onto the stack + assert(position <= UINT32_MAX); + + // Push the current sample offset onto the stack + bitstream->sample_offset_stack[bitstream->sample_offset_count++] = (uint32_t)position; + +#endif + + // Write a tag value pair for the size of this chunk + PutTagPairOptional(bitstream, tag, 0); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Update a sample size segment with the actual size of the syntax element + + This routine pops the offset in the bitstream to the most recent tag value pair + that was written into the bitstream from the sample offset stack and overwrites + the segment with the tag value pair that contains the actual size of the syntax + element. + + This routine corresponds to the routine SizeTagPop in the current codec implementation. + */ +CODEC_ERROR PopSampleSize(BITSTREAM *bitstream) +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + +#if VC5_ENABLED_PART(VC5_PART_SECTIONS) + + if (bitstream->sample_offset_count > 0) + { + TAGVALUE segment; + TAGWORD tag; + + uint32_t current_offset; + uint32_t previous_offset; + + uint32_t chunk_size; + + size_t position = GetBitstreamPosition(bitstream); + + // Get the offset to the current position in the bitstream + assert(position <= UINT32_MAX); + current_offset = (uint32_t)position; + + // Pop the offset for this chunk from the sample offset stack + previous_offset = PopSampleOffsetStack(bitstream); + + assert(previous_offset < current_offset); + + // Get the segment for the chunk written at the most recent offset + error = GetSampleOffsetSegment(bitstream, previous_offset, &segment); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + // Get the tag for the chunk segment + tag = segment.tuple.tag; + + // Should be an optional tag-value pair + assert(IsTagOptional(tag)); + if (! (IsTagOptional(tag))) { + return CODEC_ERROR_UNEXPECTED; + } + + // Convert the tag to required + tag = RequiredTag(tag); + + // Compute the size of the current chunk + chunk_size = current_offset - previous_offset; + + if (chunk_size >= 4) + { + // The chunk payload should contain an integer number of segments + assert((chunk_size % sizeof(TAGVALUE)) == 0); + + // Compute the number of segments in the chunk payload + chunk_size = (chunk_size / sizeof(TAGVALUE)) - 1; + } + else + { + chunk_size = 0; + } + + // Does this chunk have a 24-bit size field? + if (tag & CODEC_TAG_LARGE_CHUNK) + { + // Add the most significant eight bits of the size to the tag + tag |= ((chunk_size >> 16) & 0xFF); + } + + // The segment value is the least significant 16 bits of the payload size + chunk_size &= 0xFFFF; + + // Update the segment with the optional tag and chunk size + segment.tuple.tag = OptionalTag(tag); + segment.tuple.value = chunk_size; + + return PutSampleOffsetSegment(bitstream, previous_offset, segment); + } +#endif + + return CODEC_ERROR_UNEXPECTED; +} + +/*! + @brief Write the bitstream start marker + */ +CODEC_ERROR PutBitstreamStartMarker(BITSTREAM *stream) +{ + assert(stream != NULL); + if (! (stream != NULL)) { + return CODEC_ERROR_UNEXPECTED; + } + + return PutLong(stream, StartMarkerSegment); +} diff --git a/source/lib/vc5_encoder/syntax.h b/source/lib/vc5_encoder/syntax.h new file mode 100755 index 0000000..3a85fe7 --- /dev/null +++ b/source/lib/vc5_encoder/syntax.h @@ -0,0 +1,44 @@ +/*! @file syntax.h + * + * @brief Declare functions for writing bitstream syntax of encoded samples. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ENCODER_SYNTAX_H +#define ENCODER_SYNTAX_H + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR PutSampleOffsetSegment(BITSTREAM *bitstream, uint32_t offset, TAGVALUE segment); + + CODEC_ERROR PutVideoLowpassTrailer(BITSTREAM *stream); + + CODEC_ERROR PutVideoBandTrailer(BITSTREAM *stream); + + CODEC_ERROR PutTagValue(BITSTREAM *stream, TAGVALUE segment); + + CODEC_ERROR PutBitstreamStartMarker(BITSTREAM *stream); + + CODEC_ERROR PushSampleSize(BITSTREAM *bitstream, TAGWORD tag); + + CODEC_ERROR PopSampleSize(BITSTREAM *bitstream); + +#ifdef __cplusplus +} +#endif + +#endif // ENCODER_SYNTAX_H diff --git a/source/lib/vc5_encoder/vc5_encoder.c b/source/lib/vc5_encoder/vc5_encoder.c new file mode 100755 index 0000000..c28d904 --- /dev/null +++ b/source/lib/vc5_encoder/vc5_encoder.c @@ -0,0 +1,164 @@ +/*! @file vc5_encoder.c + * + * @brief Implementation of the top level vc5 encoder data structures and functions. + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +void vc5_encoder_parameters_set_default(vc5_encoder_parameters* encoding_parameters) +{ + encoding_parameters->enabled_parts = VC5_ENABLED_PARTS; + encoding_parameters->input_width = 4000; + encoding_parameters->input_height = 3000; + encoding_parameters->input_pitch = 4000; + + encoding_parameters->pixel_format = VC5_ENCODER_PIXEL_FORMAT_DEFAULT; + encoding_parameters->quality_setting = VC5_ENCODER_QUALITY_SETTING_DEFAULT; + + encoding_parameters->mem_alloc = malloc; + encoding_parameters->mem_free = free; +} + +CODEC_ERROR vc5_encoder_process(const vc5_encoder_parameters* encoding_parameters, /* vc5 encoding parameters */ + const gpr_buffer* raw_buffer, /* raw input buffer. */ + gpr_buffer* vc5_buffer, + gpr_rgb_buffer* rgb_buffer) /* rgb output buffer. */ +{ + CODEC_ERROR error = CODEC_ERROR_OKAY; + IMAGE image; + ENCODER_PARAMETERS parameters; + + STREAM bitstream_file; + + const int max_vc5_buffer_size = 10000000; + + // Initialize the data structure for passing parameters to the encoder + InitEncoderParameters(¶meters); + + { + QUANT quant_table[VC5_ENCODER_QUALITY_SETTING_COUNT][sizeof(parameters.quant_table) / sizeof(parameters.quant_table[0])] = { + {1, 24, 24, 12, 64, 64, 48, 512, 512, 768}, // CineForm Low + {1, 24, 24, 12, 48, 48, 32, 256, 256, 384}, // CineForm Medium + {1, 24, 24, 12, 32, 32, 24, 128, 128, 192}, // CineForm High + {1, 24, 24, 12, 24, 24, 12, 96, 96, 144}, // CineForm Filmscan-1 + {1, 24, 24, 12, 24, 24, 12, 64, 64, 96}, // CineForm Filmscan-X + {1, 24, 24, 12, 24, 24, 12, 32, 32, 48} // CineForm Filmscan-2 + }; + + if( encoding_parameters->quality_setting < VC5_ENCODER_QUALITY_SETTING_COUNT ) + { + memcpy(parameters.quant_table, quant_table[encoding_parameters->quality_setting], sizeof(parameters.quant_table)); + } + } + + parameters.enabled_parts = encoding_parameters->enabled_parts; + parameters.encoded.format = IMAGE_FORMAT_RAW; + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + // Test interlaced encoding using one layer per field + parameters.layer_count = 2; + parameters.progressive = 0; + parameters.decompositor = DecomposeFields; +#endif + + parameters.allocator.Alloc = encoding_parameters->mem_alloc; + parameters.allocator.Free = encoding_parameters->mem_free; + + // Check that the enabled parts are correct + error = CheckEnabledParts(¶meters.enabled_parts); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + image.buffer = raw_buffer->buffer; + + image.width = encoding_parameters->input_width; + image.height = encoding_parameters->input_height; + image.pitch = encoding_parameters->input_pitch; + image.size = image.width * image.height * 2; + image.offset = 0; + + switch( encoding_parameters->pixel_format ) + { + case VC5_ENCODER_PIXEL_FORMAT_RGGB_12: + image.format = PIXEL_FORMAT_RAW_RGGB_12; + break; + + case VC5_ENCODER_PIXEL_FORMAT_RGGB_12P: + image.format = PIXEL_FORMAT_RAW_RGGB_12P; + break; + + case VC5_ENCODER_PIXEL_FORMAT_RGGB_14: + image.format = PIXEL_FORMAT_RAW_RGGB_14; + break; + + case VC5_ENCODER_PIXEL_FORMAT_GBRG_12: + image.format = PIXEL_FORMAT_RAW_GBRG_12; + break; + + case VC5_ENCODER_PIXEL_FORMAT_GBRG_12P: + image.format = PIXEL_FORMAT_RAW_GBRG_12P; + break; + + default: + assert(0); + } + + // Set the dimensions and pixel format of the packed input image + { + parameters.input.width = image.width; + parameters.input.height = image.height; + parameters.input.format = image.format; + } + +#if VC5_ENABLED_PART(VC5_PART_LAYERS) + // Test interlaced encoding using one layer per field + parameters.layer_count = 2; + parameters.progressive = 0; + parameters.decompositor = DecomposeFields; +#endif + + vc5_buffer->buffer = encoding_parameters->mem_alloc( max_vc5_buffer_size ); + + // Open a stream to the output file + error = CreateStreamBuffer(&bitstream_file, vc5_buffer->buffer, max_vc5_buffer_size ); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + RGB_IMAGE rgb_image; + InitRGBImage(&rgb_image); + + // Encode the image into the byte stream + error = EncodeImage(&image, &bitstream_file, &rgb_image, ¶meters); + if (error != CODEC_ERROR_OKAY) { + return error; + } + + if( rgb_buffer ) + { + rgb_buffer->buffer = rgb_image.buffer; + rgb_buffer->size = rgb_image.size; + rgb_buffer->width = rgb_image.width; + rgb_buffer->height = rgb_image.height; + } + + vc5_buffer->size = bitstream_file.byte_count; + + return CODEC_ERROR_OKAY; +} diff --git a/source/lib/vc5_encoder/vc5_encoder.h b/source/lib/vc5_encoder/vc5_encoder.h new file mode 100755 index 0000000..35a9b16 --- /dev/null +++ b/source/lib/vc5_encoder/vc5_encoder.h @@ -0,0 +1,103 @@ +/*! @file vc5_encoder.h + * + * @brief Declaration of the top level vc5 encoder API. + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VC5_ENCODER_H +#define VC5_ENCODER_H + +#include "error.h" +#include "types.h" +#include "gpr_buffer.h" +#include "gpr_rgb_buffer.h" +#include "vc5_common.h" + +#ifdef __cplusplus + extern "C" { +#endif + + /*! + @brief Bayer pattern ordering for vc5 encoder processing + */ + typedef enum + { + VC5_ENCODER_PIXEL_FORMAT_RGGB_12 = 0, // RGGB 14bit pixels packed into 16bits + + VC5_ENCODER_PIXEL_FORMAT_RGGB_12P = 1, // RGGB 14bit pixels packed into 16bits + + VC5_ENCODER_PIXEL_FORMAT_RGGB_14 = 2, // RGGB 14bit pixels packed into 16bits + + VC5_ENCODER_PIXEL_FORMAT_GBRG_12 = 3, // GBRG 12bit pixels packed into 16bits + + VC5_ENCODER_PIXEL_FORMAT_GBRG_12P = 4, // GBRG 12bit pixels packed into 12bits + + VC5_ENCODER_PIXEL_FORMAT_DEFAULT = VC5_ENCODER_PIXEL_FORMAT_RGGB_14, + + } VC5_ENCODER_PIXEL_FORMAT; + + #define VC5_ENCODER_RGB_RESOLUTION_DEFAULT GPR_RGB_RESOLUTION_SIXTEENTH + + /*! + @brief Quality setting of the VC5 encoder + */ + typedef enum + { + VC5_ENCODER_QUALITY_SETTING_LOW = 0, // Low (Lowest Quality) + VC5_ENCODER_QUALITY_SETTING_MEDIUM = 1, // Medium + VC5_ENCODER_QUALITY_SETTING_HIGH = 2, // High + VC5_ENCODER_QUALITY_SETTING_FS1 = 3, // Film Scan 1 + VC5_ENCODER_QUALITY_SETTING_FSX = 4, // Film Scan X + VC5_ENCODER_QUALITY_SETTING_FS2 = 5, // Film Scan 2 (Highest Quality) + + VC5_ENCODER_QUALITY_SETTING_COUNT = 6, + + VC5_ENCODER_QUALITY_SETTING_DEFAULT = VC5_ENCODER_QUALITY_SETTING_FSX, + + } VC5_ENCODER_QUALITY_SETTING; + + /*! + @brief vc5 encoder parameters + */ + typedef struct + { + ENABLED_PARTS enabled_parts; + + unsigned int input_width; // Image Width in Components (Default: 4000) + unsigned int input_height; // Image Height in Components (Default: 3000) + int input_pitch; // Image Buffer Stride in Components (Default: 4000) + + VC5_ENCODER_PIXEL_FORMAT pixel_format; // Bayer Ordering Pattern (Default: VC5_ENCODER_BAYER_ORDERING_RGGB) + + VC5_ENCODER_QUALITY_SETTING quality_setting; // Quality setting of the encoder (Default: VC5_ENCODER_QUALITY_SETTING_FS2) + + gpr_malloc mem_alloc; // Callback function to allocate memory + + gpr_free mem_free; // Callback function to free memory + + } vc5_encoder_parameters; + + void vc5_encoder_parameters_set_default(vc5_encoder_parameters* encoding_parameters); + + CODEC_ERROR vc5_encoder_process(const vc5_encoder_parameters* encoding_parameters, /* vc5 encoding parameters */ + const gpr_buffer* raw_buffer, /* raw input buffer. */ + gpr_buffer* vc5_buffer, + gpr_rgb_buffer* rgb_buffer); /* vc5 output buffer. */ + +#ifdef __cplusplus + } +#endif + +#endif // VC5_ENCODER_H diff --git a/source/lib/vc5_encoder/vlc.c b/source/lib/vc5_encoder/vlc.c new file mode 100755 index 0000000..b89bbd0 --- /dev/null +++ b/source/lib/vc5_encoder/vlc.c @@ -0,0 +1,94 @@ +/*! @file vlc.c + * + * @brief Implementation of routines to insert variable length codes into the bitstream + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "headers.h" + +/*! + @brief Write the codewords for a run of zeros into the bitstream + + The codebook contains codewords for a few runs of zeros. This routine writes + multiple codewords into the bitstream until the specified number of zeros has + been written into the bitstream. +*/ +CODEC_ERROR PutZeros(BITSTREAM *stream, const RUNS_TABLE *runs_table, uint32_t count) +{ + // Get the length of the codebook and a pointer to the entries + uint32_t length = runs_table->length; + RLC *rlc = (RLC *)((uint8_t *)runs_table + sizeof(RUNS_TABLE)); + int index; + + // Output one or more run lengths until the run is finished + while (count > 0) + { + // Index into the codebook to get a run length code that covers most of the run + index = (count < length) ? count : length - 1; + + // Output the run length code + PutBits(stream, rlc[index].bits, rlc[index].size); + + // Reduce the length of the run by the amount output + count -= rlc[index].count; + } + + // Should have output enough runs to cover the run of zeros + assert(count == 0); + + return CODEC_ERROR_OKAY; +} + +/*! + @brief Insert a special codeword into the bitstream + + The codebook contains special codewords in addition to the codebook + entries for coefficient magnitudes and runs of zeros. Special codewords + are used to mark important locations in the bitstream. Currently, + the only special codeword is the one that marks the end of an encoded + band. +*/ +CODEC_ERROR PutSpecial(BITSTREAM *stream, const CODEBOOK *codebook, SPECIAL_MARKER marker) +{ + int codebook_length = codebook->length; + RLV *codebook_entry = (RLV *)((uint8_t *)codebook + sizeof(CODEBOOK)); + + int index; + + // Find the special code that corresponds to the marker + for (index = 0; index < codebook_length; index++) + { + // Is this entry a special code? + if (codebook_entry[index].count != 0) { + continue; + } + + // Is this entry the special code for the marker? + if (codebook_entry[index].value == marker) { + break; + } + } + assert(index < codebook_length); + if (! (index < codebook_length)) { + // Did not find the special code for the marker in the codebook + return CODEC_ERROR_INVALID_MARKER; + } + + PutBits(stream, codebook_entry[index].bits, codebook_entry[index].size); + + return CODEC_ERROR_OKAY; +} diff --git a/source/lib/vc5_encoder/vlc.h b/source/lib/vc5_encoder/vlc.h new file mode 100755 index 0000000..4947060 --- /dev/null +++ b/source/lib/vc5_encoder/vlc.h @@ -0,0 +1,138 @@ +/*! @file vlc.h + * + * @brief Declaration of the data structures for variable-length encoding + * + * @version 1.0.0 + * + * (C) Copyright 2018 GoPro Inc (http://gopro.com/). + * + * Licensed under either: + * - Apache License, Version 2.0, http://www.apache.org/licenses/LICENSE-2.0 + * - MIT license, http://opensource.org/licenses/MIT + * at your option. + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef VLC_H +#define VLC_H + +/*! + @brief Codebook entries for arbitrary runs and values + + The codebook data structure allows runs of an arbitrary value, + but all codec implementations only use runs of zeros. The + codeword for a non-zero value is followed by the sign bit. + + @todo Could add the sign bit to the magnitude entries in this + table if it improves performance or makes the code more clear. +*/ +typedef struct _rlv { + uint_fast8_t size; //!< Size of code word in bits + uint32_t bits; //!< Code word bits right justified + uint32_t count; //!< Run length + int32_t value; //!< Run value (unsigned) +} RLV; + +/*! + @brief Declaration of a codebook + + This data structure is often called the master codebook to distinguish + it from the encoding tables that are derived from the codebook. The + codebook has a header that is immediately followed by the codebook entries. + Each entry is an @ref RLV data structure that contains the codeword and + the size of the codeword in bits. Each codeword represent a run length + and value. The current codec implementation only supports runs of zeros, + so the run length is one for non-zero values. A non-zero value is an + unsigned coefficient magnitude. Special codewords that mark significant + locations in the bitstream are indicated by a run length of zero and the + value indicates the type of marker. + + The codebook is generated by a separate program that takes as input a table + of the frequencies of coefficient magnitudes and runs of zeros. +*/ +typedef struct _codebook +{ + uint32_t length; //!< Number of entries in the code book + // The length is followed by the RLV entries +} CODEBOOK; + +//! Macro used to define the codebook generated by the Huffman program +#define RLVTABLE(n) \ + static struct \ + { \ + uint32_t length; \ + RLV entries[n]; \ + } + +/*! + @brief Table of codewords for coefficient magnitudes + + The entries in this table are indexed by the coefficient magnitude. + + This table is derived from the master codebook by sorting the entries + for coefficient magnitudes into increasing order. Each entry in the + table is a codeword and its size in bits. +*/ +typedef struct _magnitude_table +{ + uint32_t length; //!< Number of entries in the encoding table + // The length is followed by the VLE entries +} MAGS_TABLE; + +/*! + @brief Entry in the table for encoding coefficients magnitudes + + Each entry is the codeword and its size in bits. The typename VLE + stands for variable length encoding to distinguish this entry from + the data structures for variable length coding in general. +*/ +typedef struct _vle { + uint_fast8_t size; //!< Size of code word in bits + uint32_t bits; //!< Code words bits (right justified) +} VLE; + +/*! + @brief Table of codewords for runs of zeros + + The entries in this table are indexed by length of the run of zeros. + + This table is derived from the master codebook by concatenating codewords + for runs of zeros to form a codeword a run with the specified length. + + Each entry in the table is a codeword and its size in bits and the number + of zeros that are that are not included in the run represented by the + codeword. +*/ +typedef struct _runs_table +{ + uint32_t length; //!< Number of entries in the encoding table + // The length is followed by the RLC entries +} RUNS_TABLE; + +/*! + @brief Entry in the table for encoding runs of zeros +*/ +typedef struct _rlc { // Codebook entries for runs of zeros + uint_fast8_t size; //!< Size of code word in bits + uint32_t count; //!< Remaining length of the run + uint32_t bits; //!< Code word bits (right justified) +} RLC; + + +#ifdef __cplusplus +extern "C" { +#endif + + CODEC_ERROR PutZeros(BITSTREAM *stream, const RUNS_TABLE *codebook, uint32_t count); + CODEC_ERROR PutSpecial(BITSTREAM *stream, const CODEBOOK *codebook, SPECIAL_MARKER marker); + +#ifdef __cplusplus +} +#endif + +#endif // VLC_H diff --git a/source/lib/xmp_core/BSD-License.txt b/source/lib/xmp_core/BSD-License.txt new file mode 100644 index 0000000..07b967c --- /dev/null +++ b/source/lib/xmp_core/BSD-License.txt @@ -0,0 +1,32 @@ +The BSD License + +Copyright (c) 1999 - 2014, Adobe Systems Incorporated +All rights reserved. + +Redistribution and use in source and binary forms, with or +without modification, are permitted provided that the following +conditions are met: + +* Redistributions of source code must retain the above copyright notice, +this list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, +this list of conditions and the following disclaimer in the documentation +and/or other materials provided with the distribution. + +* Neither the name of Adobe Systems Incorporated, nor the names of its +contributors may be used to endorse or promote products derived from this +software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/source/lib/xmp_core/CMakeLists.txt b/source/lib/xmp_core/CMakeLists.txt new file mode 100644 index 0000000..5bee2e4 --- /dev/null +++ b/source/lib/xmp_core/CMakeLists.txt @@ -0,0 +1,32 @@ +# library +set( LIB_NAME xmp_core ) + +# get source files +file( GLOB BASE_SRC_FILES "*.cpp" ) + +# get include files +file( GLOB BASE_INC_FILES "*.h" "public/include/*.h" "public/include/*.hpp" "public/include/client-glue/*.hpp" ) + +# get all source files +set( SRC_FILES ${BASE_SRC_FILES} ) + +# get all include files +set( INC_FILES ${BASE_INC_FILES} ) + +# add include files from other folders +include_directories( "../md5_lib" ) +include_directories( "../expat_lib" ) +include_directories( "public/include" ) + +# library +add_library( ${LIB_NAME} STATIC ${SRC_FILES} ${INC_FILES} ) + +# define compile time definitions +target_compile_definitions( ${LIB_NAME} PUBLIC XML_STATIC=1 ) + +SET(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libstdc++") + +target_link_libraries( ${LIB_NAME} ) + +# set the folder where to place the projects +set_target_properties( ${LIB_NAME} PROPERTIES FOLDER lib ) diff --git a/source/lib/xmp_core/ExpatAdapter.cpp b/source/lib/xmp_core/ExpatAdapter.cpp new file mode 100644 index 0000000..c0388c3 --- /dev/null +++ b/source/lib/xmp_core/ExpatAdapter.cpp @@ -0,0 +1,522 @@ +// ================================================================================================= +// Copyright 2005 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! Must be the first #include! +#include "XMPCore_Impl.hpp" + +#include "ExpatAdapter.hpp" +#include "XMPMeta.hpp" + +#include "expat.h" +#include + +using namespace std; + +#if XMP_WinBuild + #pragma warning ( disable : 4996 ) // '...' was declared deprecated +#endif + +// *** Set memory handlers. + +#ifndef DumpXMLParseEvents + #define DumpXMLParseEvents 0 +#endif + +#define FullNameSeparator '@' + +// ================================================================================================= + +static void StartNamespaceDeclHandler ( void * userData, XMP_StringPtr prefix, XMP_StringPtr uri ); +static void EndNamespaceDeclHandler ( void * userData, XMP_StringPtr prefix ); + +static void StartElementHandler ( void * userData, XMP_StringPtr name, XMP_StringPtr* attrs ); +static void EndElementHandler ( void * userData, XMP_StringPtr name ); + +static void CharacterDataHandler ( void * userData, XMP_StringPtr cData, int len ); +static void StartCdataSectionHandler ( void * userData ); +static void EndCdataSectionHandler ( void * userData ); + +static void ProcessingInstructionHandler ( void * userData, XMP_StringPtr target, XMP_StringPtr data ); +static void CommentHandler ( void * userData, XMP_StringPtr comment ); + +#if BanAllEntityUsage + + // For now we do this by banning DOCTYPE entirely. This is easy and consistent with what is + // available in recent Java XML parsers. Another, somewhat less drastic, approach would be to + // ban all entity declarations. We can't allow declarations and ban references, Expat does not + // call the SkippedEntityHandler for references in attribute values. + + // ! Standard entities (&, <, >, ", ', and numeric character references) are + // ! not banned. Expat handles them transparently no matter what. + + static void StartDoctypeDeclHandler ( void * userData, XMP_StringPtr doctypeName, + XMP_StringPtr sysid, XMP_StringPtr pubid, int has_internal_subset ); + +#endif + +// ================================================================================================= + +extern "C" ExpatAdapter * XMP_NewExpatAdapter ( bool useGlobalNamespaces ) +{ + + return new ExpatAdapter ( useGlobalNamespaces ); + +} // XMP_NewExpatAdapter + +// ================================================================================================= + +ExpatAdapter::ExpatAdapter ( bool useGlobalNamespaces ) : parser(0), registeredNamespaces(0) +{ + + #if XMP_DebugBuild + this->elemNesting = 0; + #if DumpXMLParseEvents + if ( this->parseLog == 0 ) this->parseLog = stdout; + #endif + #endif + + this->parser = XML_ParserCreateNS ( 0, FullNameSeparator ); + if ( this->parser == 0 ) { + XMP_Error error(kXMPErr_NoMemory, "Failure creating Expat parser" ); + this->NotifyClient ( kXMPErrSev_ProcessFatal, error ); + }else{ + if ( useGlobalNamespaces ) { + this->registeredNamespaces = sRegisteredNamespaces; + } else { + this->registeredNamespaces = new XMP_NamespaceTable ( *sRegisteredNamespaces ); + } + + XML_SetUserData ( this->parser, this ); + + XML_SetNamespaceDeclHandler ( this->parser, StartNamespaceDeclHandler, EndNamespaceDeclHandler ); + XML_SetElementHandler ( this->parser, StartElementHandler, EndElementHandler ); + + XML_SetCharacterDataHandler ( this->parser, CharacterDataHandler ); + XML_SetCdataSectionHandler ( this->parser, StartCdataSectionHandler, EndCdataSectionHandler ); + + XML_SetProcessingInstructionHandler ( this->parser, ProcessingInstructionHandler ); + XML_SetCommentHandler ( this->parser, CommentHandler ); + + #if BanAllEntityUsage + XML_SetStartDoctypeDeclHandler ( this->parser, StartDoctypeDeclHandler ); + isAborted = false; + #endif + + this->parseStack.push_back ( &this->tree ); // Push the XML root node. + } +} // ExpatAdapter::ExpatAdapter + +// ================================================================================================= + +ExpatAdapter::~ExpatAdapter() +{ + + if ( this->parser != 0 ) XML_ParserFree ( this->parser ); + this->parser = 0; + + if ( this->registeredNamespaces != sRegisteredNamespaces ) delete ( this->registeredNamespaces ); + this->registeredNamespaces = 0; + +} // ExpatAdapter::~ExpatAdapter + +// ================================================================================================= + +#if XMP_DebugBuild + static XMP_VarString sExpatMessage; +#endif + +static const char * kOneSpace = " "; + +void ExpatAdapter::ParseBuffer ( const void * buffer, size_t length, bool last /* = true */ ) +{ + enum XML_Status status; + + if ( length == 0 ) { // Expat does not like empty buffers. + if ( ! last ) return; + buffer = kOneSpace; + length = 1; + } + + status = XML_Parse ( this->parser, (const char *)buffer, length, last ); + + #if BanAllEntityUsage + if ( this->isAborted ) { + XMP_Error error(kXMPErr_BadXML, "DOCTYPE is not allowed" ) + this->NotifyClient ( kXMPErrSev_Recoverable, error ); + } + #endif + + if ( status != XML_STATUS_OK ) { + + XMP_StringPtr errMsg = "XML parsing failure"; + + #if 0 // XMP_DebugBuild // Disable for now to make test output uniform. Restore later with thread safety. + + // *** This is a good candidate for a callback error notification mechanism. + // *** This code is not thread safe, the sExpatMessage isn't locked. But that's OK for debug usage. + + enum XML_Error expatErr = XML_GetErrorCode ( this->parser ); + const char * expatMsg = XML_ErrorString ( expatErr ); + int errLine = XML_GetCurrentLineNumber ( this->parser ); + + char msgBuffer[1000]; + // AUDIT: Use of sizeof(msgBuffer) for snprintf length is safe. + snprintf ( msgBuffer, sizeof(msgBuffer), "# Expat error %d at line %d, \"%s\"", expatErr, errLine, expatMsg ); + sExpatMessage = msgBuffer; + errMsg = sExpatMessage.c_str(); + + #if DumpXMLParseEvents + if ( this->parseLog != 0 ) fprintf ( this->parseLog, "%s\n", errMsg, expatErr, errLine, expatMsg ); + #endif + + #endif + + XMP_Error error(kXMPErr_BadXML, errMsg); + this->NotifyClient ( kXMPErrSev_Recoverable, error ); + + } + +} // ExpatAdapter::ParseBuffer + +// ================================================================================================= +// ================================================================================================= + +#if XMP_DebugBuild & DumpXMLParseEvents + + static inline void PrintIndent ( FILE * file, size_t count ) + { + for ( ; count > 0; --count ) fprintf ( file, " " ); + } + +#endif + +// ================================================================================================= + +static void SetQualName ( ExpatAdapter * thiz, XMP_StringPtr fullName, XML_Node * node ) +{ + // Expat delivers the full name as a catenation of namespace URI, separator, and local name. + + // As a compatibility hack, an "about" or "ID" attribute of an rdf:Description element is + // changed to "rdf:about" or rdf:ID. Easier done here than in the RDF recognizer. + + // As a bug fix hack, change a URI of "http://purl.org/dc/1.1/" to ""http://purl.org/dc/elements/1.1/. + // Early versions of Flash that put XMP in SWF used a bad URI for the dc: namespace. + + // ! This code presumes the RDF namespace prefix is "rdf". + + size_t sepPos = strlen(fullName); + for ( --sepPos; sepPos > 0; --sepPos ) { + if ( fullName[sepPos] == FullNameSeparator ) break; + } + + if ( fullName[sepPos] == FullNameSeparator ) { + + XMP_StringPtr prefix; + XMP_StringLen prefixLen; + XMP_StringPtr localPart = fullName + sepPos + 1; + + node->ns.assign ( fullName, sepPos ); + if ( node->ns == "http://purl.org/dc/1.1/" ) node->ns = "http://purl.org/dc/elements/1.1/"; + + bool found = thiz->registeredNamespaces->GetPrefix ( node->ns.c_str(), &prefix, &prefixLen ); + if ( ! found ) { + XMP_Error error(kXMPErr_ExternalFailure, "Unknown URI in Expat full name" ); + thiz->NotifyClient ( kXMPErrSev_OperationFatal, error ); + } + node->nsPrefixLen = prefixLen; // ! Includes the ':'. + + node->name = prefix; + node->name += localPart; + + } else { + + node->name = fullName; // The name is not in a namespace. + + if ( node->parent->name == "rdf:Description" ) { + if ( node->name == "about" ) { + node->ns = kXMP_NS_RDF; + node->name = "rdf:about"; + node->nsPrefixLen = 4; // ! Include the ':'. + } else if ( node->name == "ID" ) { + node->ns = kXMP_NS_RDF; + node->name = "rdf:ID"; + node->nsPrefixLen = 4; // ! Include the ':'. + } + } + + } + +} // SetQualName + +// ================================================================================================= + +static void StartNamespaceDeclHandler ( void * userData, XMP_StringPtr prefix, XMP_StringPtr uri ) +{ + IgnoreParam(userData); + + // As a bug fix hack, change a URI of "http://purl.org/dc/1.1/" to ""http://purl.org/dc/elements/1.1/. + // Early versions of Flash that put XMP in SWF used a bad URI for the dc: namespace. + + ExpatAdapter * thiz = (ExpatAdapter*)userData; + + if ( prefix == 0 ) prefix = "_dflt_"; // Have default namespace. + if ( uri == 0 ) return; // Ignore, have xmlns:pre="", no URI to register. + + #if XMP_DebugBuild & DumpXMLParseEvents + if ( thiz->parseLog != 0 ) { + PrintIndent ( thiz->parseLog, thiz->elemNesting ); + fprintf ( thiz->parseLog, "StartNamespace: %s - \"%s\"\n", prefix, uri ); + } + #endif + + if ( XMP_LitMatch ( uri, "http://purl.org/dc/1.1/" ) ) uri = "http://purl.org/dc/elements/1.1/"; + (void) thiz->registeredNamespaces->Define ( uri, prefix, 0, 0 ); + +} // StartNamespaceDeclHandler + +// ================================================================================================= + +static void EndNamespaceDeclHandler ( void * userData, XMP_StringPtr prefix ) +{ + IgnoreParam(userData); + + #if XMP_DebugBuild & DumpXMLParseEvents // Avoid unused variable warning. + ExpatAdapter * thiz = (ExpatAdapter*)userData; + #endif + + if ( prefix == 0 ) prefix = "_dflt_"; // Have default namespace. + + #if XMP_DebugBuild & DumpXMLParseEvents + if ( thiz->parseLog != 0 ) { + PrintIndent ( thiz->parseLog, thiz->elemNesting ); + fprintf ( thiz->parseLog, "EndNamespace: %s\n", prefix ); + } + #endif + + // ! Nothing to do, Expat has done all of the XML processing. + +} // EndNamespaceDeclHandler + +// ================================================================================================= + +static void StartElementHandler ( void * userData, XMP_StringPtr name, XMP_StringPtr* attrs ) +{ + XMP_Assert ( attrs != 0 ); + ExpatAdapter * thiz = (ExpatAdapter*)userData; + + size_t attrCount = 0; + for ( XMP_StringPtr* a = attrs; *a != 0; ++a ) ++attrCount; + if ( (attrCount & 1) != 0 ) { + XMP_Error error(kXMPErr_ExternalFailure, "Expat attribute info has odd length"); + thiz->NotifyClient ( kXMPErrSev_OperationFatal, error ); + } + attrCount = attrCount/2; // They are name/value pairs. + + #if XMP_DebugBuild & DumpXMLParseEvents + if ( thiz->parseLog != 0 ) { + PrintIndent ( thiz->parseLog, thiz->elemNesting ); + fprintf ( thiz->parseLog, "StartElement: %s, %d attrs", name, attrCount ); + for ( XMP_StringPtr* attr = attrs; *attr != 0; attr += 2 ) { + XMP_StringPtr attrName = *attr; + XMP_StringPtr attrValue = *(attr+1); + fprintf ( thiz->parseLog, ", %s = \"%s\"", attrName, attrValue ); + } + fprintf ( thiz->parseLog, "\n" ); + } + #endif + + XML_Node * parentNode = thiz->parseStack.back(); + XML_Node * elemNode = new XML_Node ( parentNode, "", kElemNode ); + + SetQualName ( thiz, name, elemNode ); + + for ( XMP_StringPtr* attr = attrs; *attr != 0; attr += 2 ) { + + XMP_StringPtr attrName = *attr; + XMP_StringPtr attrValue = *(attr+1); + XML_Node * attrNode = new XML_Node ( elemNode, "", kAttrNode ); + + SetQualName ( thiz, attrName, attrNode ); + attrNode->value = attrValue; + if ( attrNode->name == "xml:lang" ) NormalizeLangValue ( &attrNode->value ); + elemNode->attrs.push_back ( attrNode ); + + } + + parentNode->content.push_back ( elemNode ); + thiz->parseStack.push_back ( elemNode ); + + if ( elemNode->name == "rdf:RDF" ) { + thiz->rootNode = elemNode; + ++thiz->rootCount; + } + #if XMP_DebugBuild + ++thiz->elemNesting; + #endif + +} // StartElementHandler + +// ================================================================================================= + +static void EndElementHandler ( void * userData, XMP_StringPtr name ) +{ + IgnoreParam(name); + + ExpatAdapter * thiz = (ExpatAdapter*)userData; + + #if XMP_DebugBuild + --thiz->elemNesting; + #endif + (void) thiz->parseStack.pop_back(); + + #if XMP_DebugBuild & DumpXMLParseEvents + if ( thiz->parseLog != 0 ) { + PrintIndent ( thiz->parseLog, thiz->elemNesting ); + fprintf ( thiz->parseLog, "EndElement: %s\n", name ); + } + #endif + +} // EndElementHandler + +// ================================================================================================= + +static void CharacterDataHandler ( void * userData, XMP_StringPtr cData, int len ) +{ + ExpatAdapter * thiz = (ExpatAdapter*)userData; + + if ( (cData == 0) || (len == 0) ) { cData = ""; len = 0; } + + #if XMP_DebugBuild & DumpXMLParseEvents + if ( thiz->parseLog != 0 ) { + PrintIndent ( thiz->parseLog, thiz->elemNesting ); + fprintf ( thiz->parseLog, "CharContent: \"" ); + for ( int i = 0; i < len; ++i ) fprintf ( thiz->parseLog, "%c", cData[i] ); + fprintf ( thiz->parseLog, "\"\n" ); + } + #endif + + XML_Node * parentNode = thiz->parseStack.back(); + XML_Node * cDataNode = new XML_Node ( parentNode, "", kCDataNode ); + + cDataNode->value.assign ( cData, len ); + parentNode->content.push_back ( cDataNode ); + +} // CharacterDataHandler + +// ================================================================================================= + +static void StartCdataSectionHandler ( void * userData ) +{ + IgnoreParam(userData); + + #if XMP_DebugBuild & DumpXMLParseEvents // Avoid unused variable warning. + ExpatAdapter * thiz = (ExpatAdapter*)userData; + #endif + + #if XMP_DebugBuild & DumpXMLParseEvents + if ( thiz->parseLog != 0 ) { + PrintIndent ( thiz->parseLog, thiz->elemNesting ); + fprintf ( thiz->parseLog, "StartCDATA\n" ); + } + #endif + + // *** Since markup isn't recognized inside CDATA, this affects XMP's double escaping. + +} // StartCdataSectionHandler + +// ================================================================================================= + +static void EndCdataSectionHandler ( void * userData ) +{ + IgnoreParam(userData); + + #if XMP_DebugBuild & DumpXMLParseEvents // Avoid unused variable warning. + ExpatAdapter * thiz = (ExpatAdapter*)userData; + #endif + + #if XMP_DebugBuild & DumpXMLParseEvents + if ( thiz->parseLog != 0 ) { + PrintIndent ( thiz->parseLog, thiz->elemNesting ); + fprintf ( thiz->parseLog, "EndCDATA\n" ); + } + #endif + +} // EndCdataSectionHandler + +// ================================================================================================= + +static void ProcessingInstructionHandler ( void * userData, XMP_StringPtr target, XMP_StringPtr data ) +{ + XMP_Assert ( target != 0 ); + ExpatAdapter * thiz = (ExpatAdapter*)userData; + + if ( ! XMP_LitMatch ( target, "xpacket" ) ) return; // Ignore all PIs except the XMP packet wrapper. + if ( data == 0 ) data = ""; + + #if XMP_DebugBuild & DumpXMLParseEvents + if ( thiz->parseLog != 0 ) { + PrintIndent ( thiz->parseLog, thiz->elemNesting ); + fprintf ( thiz->parseLog, "PI: %s - \"%s\"\n", target, data ); + } + #endif + + XML_Node * parentNode = thiz->parseStack.back(); + XML_Node * piNode = new XML_Node ( parentNode, target, kPINode ); + + piNode->value.assign ( data ); + parentNode->content.push_back ( piNode ); + +} // ProcessingInstructionHandler + +// ================================================================================================= + +static void CommentHandler ( void * userData, XMP_StringPtr comment ) +{ + IgnoreParam(userData); + + #if XMP_DebugBuild & DumpXMLParseEvents // Avoid unused variable warning. + ExpatAdapter * thiz = (ExpatAdapter*)userData; + #endif + + if ( comment == 0 ) comment = ""; + + #if XMP_DebugBuild & DumpXMLParseEvents + if ( thiz->parseLog != 0 ) { + PrintIndent ( thiz->parseLog, thiz->elemNesting ); + fprintf ( thiz->parseLog, "Comment: \"%s\"\n", comment ); + } + #endif + + // ! Comments are ignored. + +} // CommentHandler + +// ================================================================================================= + +#if BanAllEntityUsage +static void StartDoctypeDeclHandler ( void * userData, XMP_StringPtr doctypeName, + XMP_StringPtr sysid, XMP_StringPtr pubid, int has_internal_subset ) +{ + IgnoreParam(userData); + + ExpatAdapter * thiz = (ExpatAdapter*)userData; + + #if XMP_DebugBuild & DumpXMLParseEvents // Avoid unused variable warning. + if ( thiz->parseLog != 0 ) { + PrintIndent ( thiz->parseLog, thiz->elemNesting ); + fprintf ( thiz->parseLog, "DocType: \"%s\"\n", doctypeName ); + } + #endif + + thiz->isAborted = true; // ! Can't throw an exception across the plain C Expat frames. + (void) XML_StopParser ( thiz->parser, XML_FALSE /* not resumable */ ); + +} // StartDoctypeDeclHandler +#endif + +// ================================================================================================= diff --git a/source/lib/xmp_core/ExpatAdapter.hpp b/source/lib/xmp_core/ExpatAdapter.hpp new file mode 100644 index 0000000..4e03436 --- /dev/null +++ b/source/lib/xmp_core/ExpatAdapter.hpp @@ -0,0 +1,59 @@ +#ifndef __ExpatAdapter_hpp__ +#define __ExpatAdapter_hpp__ + +// ================================================================================================= +// Copyright 2005 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! Must be the first #include! +#include "XMLParserAdapter.hpp" + +// ================================================================================================= +// Derived XML parser adapter for Expat. +// ================================================================================================= + +#ifndef BanAllEntityUsage + #define BanAllEntityUsage 0 +#endif + +struct XML_ParserStruct; // ! Hack to avoid exposing expat.h to all clients. +typedef struct XML_ParserStruct *XML_Parser; + +class ExpatAdapter : public XMLParserAdapter { +public: + + XML_Parser parser; + XMP_NamespaceTable * registeredNamespaces; + + #if BanAllEntityUsage + bool isAborted; + #endif + + #if XMP_DebugBuild + size_t elemNesting; + #endif + + static const bool kUseGlobalNamespaces = true; + static const bool kUseLocalNamespaces = false; + + ExpatAdapter ( bool useGlobalNamespaces ); + virtual ~ExpatAdapter(); + + void ParseBuffer ( const void * buffer, size_t length, bool last = true ); + +private: + + ExpatAdapter() : registeredNamespaces(0) {}; // ! Force use of constructor with namespace parameter. + +}; + +extern "C" ExpatAdapter * +XMP_PUBLIC XMP_NewExpatAdapter ( bool useGlobalNamespaces ); + +// ================================================================================================= + +#endif // __ExpatAdapter_hpp__ diff --git a/source/lib/xmp_core/ParseRDF.cpp b/source/lib/xmp_core/ParseRDF.cpp new file mode 100644 index 0000000..0b69e31 --- /dev/null +++ b/source/lib/xmp_core/ParseRDF.cpp @@ -0,0 +1,1459 @@ +// ================================================================================================= +// Copyright 2004 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! This must be the first include! +#include "XMPCore_Impl.hpp" +#include "XMPMeta.hpp" +#include "ExpatAdapter.hpp" + +#include + +#if DEBUG + #include +#endif + +using namespace std; + +#if XMP_WinBuild + #pragma warning ( disable : 4189 ) // local variable is initialized but not referenced + #pragma warning ( disable : 4505 ) // unreferenced local function has been removed +#endif + +// ================================================================================================= + +// *** This might be faster and use less memory as a state machine. A big advantage of building an +// *** XML tree though is easy lookahead during the recursive descent processing. + +// *** It would be nice to give a line number or byte offset in the exception messages. + + +// 7 RDF/XML Grammar (from http://www.w3.org/TR/rdf-syntax-grammar/#section-Infoset-Grammar) +// +// 7.1 Grammar summary +// +// 7.2.2 coreSyntaxTerms +// rdf:RDF | rdf:ID | rdf:about | rdf:parseType | rdf:resource | rdf:nodeID | rdf:datatype +// +// 7.2.3 syntaxTerms +// coreSyntaxTerms | rdf:Description | rdf:li +// +// 7.2.4 oldTerms +// rdf:aboutEach | rdf:aboutEachPrefix | rdf:bagID +// +// 7.2.5 nodeElementURIs +// anyURI - ( coreSyntaxTerms | rdf:li | oldTerms ) +// +// 7.2.6 propertyElementURIs +// anyURI - ( coreSyntaxTerms | rdf:Description | oldTerms ) +// +// 7.2.7 propertyAttributeURIs +// anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms ) +// +// 7.2.8 doc +// root ( document-element == RDF, children == list ( RDF ) ) +// +// 7.2.9 RDF +// start-element ( URI == rdf:RDF, attributes == set() ) +// nodeElementList +// end-element() +// +// 7.2.10 nodeElementList +// ws* ( nodeElement ws* )* +// +// 7.2.11 nodeElement +// start-element ( URI == nodeElementURIs, +// attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) ) +// propertyEltList +// end-element() +// +// 7.2.12 ws +// A text event matching white space defined by [XML] definition White Space Rule [3] S in section Common Syntactic Constructs. +// +// 7.2.13 propertyEltList +// ws* ( propertyElt ws* )* +// +// 7.2.14 propertyElt +// resourcePropertyElt | literalPropertyElt | parseTypeLiteralPropertyElt | +// parseTypeResourcePropertyElt | parseTypeCollectionPropertyElt | parseTypeOtherPropertyElt | emptyPropertyElt +// +// 7.2.15 resourcePropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) ) +// ws* nodeElement ws* +// end-element() +// +// 7.2.16 literalPropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) ) +// text() +// end-element() +// +// 7.2.17 parseTypeLiteralPropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseLiteral ) ) +// literal +// end-element() +// +// 7.2.18 parseTypeResourcePropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) ) +// propertyEltList +// end-element() +// +// 7.2.19 parseTypeCollectionPropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseCollection ) ) +// nodeElementList +// end-element() +// +// 7.2.20 parseTypeOtherPropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) ) +// propertyEltList +// end-element() +// +// 7.2.21 emptyPropertyElt +// start-element ( URI == propertyElementURIs, +// attributes == set ( idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) ) +// end-element() +// +// 7.2.22 idAttr +// attribute ( URI == rdf:ID, string-value == rdf-id ) +// +// 7.2.23 nodeIdAttr +// attribute ( URI == rdf:nodeID, string-value == rdf-id ) +// +// 7.2.24 aboutAttr +// attribute ( URI == rdf:about, string-value == URI-reference ) +// +// 7.2.25 propertyAttr +// attribute ( URI == propertyAttributeURIs, string-value == anyString ) +// +// 7.2.26 resourceAttr +// attribute ( URI == rdf:resource, string-value == URI-reference ) +// +// 7.2.27 datatypeAttr +// attribute ( URI == rdf:datatype, string-value == URI-reference ) +// +// 7.2.28 parseLiteral +// attribute ( URI == rdf:parseType, string-value == "Literal") +// +// 7.2.29 parseResource +// attribute ( URI == rdf:parseType, string-value == "Resource") +// +// 7.2.30 parseCollection +// attribute ( URI == rdf:parseType, string-value == "Collection") +// +// 7.2.31 parseOther +// attribute ( URI == rdf:parseType, string-value == anyString - ("Resource" | "Literal" | "Collection") ) +// +// 7.2.32 URI-reference +// An RDF URI Reference. +// +// 7.2.33 literal +// Any XML element content that is allowed according to [XML] definition Content of Elements Rule [43] content +// in section 3.1 Start-Tags, End-Tags, and Empty-Element Tags. +// +// 7.2.34 rdf-id +// An attribute string-value matching any legal [XML-NS] token NCName. + + +// ================================================================================================= +// Primary Parsing Functions +// ========================= +// +// Each of these is responsible for recognizing an RDF syntax production and adding the appropriate +// structure to the XMP tree. They simply return for success, failures will throw an exception. The +// class exists only to provide access to the error notification object. + +class RDF_Parser { +public: + + void RDF ( XMP_Node * xmpTree, const XML_Node & xmlNode ); + + void NodeElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel ); + + void NodeElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ); + + void NodeElementAttrs ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ); + + void PropertyElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel ); + + void PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ); + + void ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ); + + void LiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ); + + void ParseTypeLiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ); + + void ParseTypeResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ); + + void ParseTypeCollectionPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ); + + void ParseTypeOtherPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ); + + void EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ); + + RDF_Parser ( XMPMeta::ErrorCallbackInfo * ec ) : errorCallback(ec) {}; + +private: + + RDF_Parser() { + + errorCallback = NULL; + + }; // Hidden on purpose. + + XMPMeta::ErrorCallbackInfo * errorCallback; + + XMP_Node * AddChildNode ( XMP_Node * xmpParent, const XML_Node & xmlNode, const XMP_StringPtr value, bool isTopLevel ); + + XMP_Node * AddQualifierNode ( XMP_Node * xmpParent, const XMP_VarString & name, const XMP_VarString & value ); + + XMP_Node * AddQualifierNode ( XMP_Node * xmpParent, const XML_Node & attr ); + + void FixupQualifiedNode ( XMP_Node * xmpParent ); + +}; + +enum { kIsTopLevel = true, kNotTopLevel = false }; + +// ================================================================================================= + +typedef XMP_Uns8 RDFTermKind; + +// *** Logic might be safer with just masks. + +enum { + kRDFTerm_Other = 0, + kRDFTerm_RDF = 1, // Start of coreSyntaxTerms. + kRDFTerm_ID = 2, + kRDFTerm_about = 3, + kRDFTerm_parseType = 4, + kRDFTerm_resource = 5, + kRDFTerm_nodeID = 6, + kRDFTerm_datatype = 7, // End of coreSyntaxTerms. + kRDFTerm_Description = 8, // Start of additions for syntaxTerms. + kRDFTerm_li = 9, // End of of additions for syntaxTerms. + kRDFTerm_aboutEach = 10, // Start of oldTerms. + kRDFTerm_aboutEachPrefix = 11, + kRDFTerm_bagID = 12, // End of oldTerms. + + kRDFTerm_FirstCore = kRDFTerm_RDF, + kRDFTerm_LastCore = kRDFTerm_datatype, + kRDFTerm_FirstSyntax = kRDFTerm_FirstCore, // ! Yes, the syntax terms include the core terms. + kRDFTerm_LastSyntax = kRDFTerm_li, + kRDFTerm_FirstOld = kRDFTerm_aboutEach, + kRDFTerm_LastOld = kRDFTerm_bagID +}; + +enum { + kRDFMask_Other = 1 << kRDFTerm_Other, + kRDFMask_RDF = 1 << kRDFTerm_RDF, + kRDFMask_ID = 1 << kRDFTerm_ID, + kRDFMask_about = 1 << kRDFTerm_about, + kRDFMask_parseType = 1 << kRDFTerm_parseType, + kRDFMask_resource = 1 << kRDFTerm_resource, + kRDFMask_nodeID = 1 << kRDFTerm_nodeID, + kRDFMask_datatype = 1 << kRDFTerm_datatype, + kRDFMask_Description = 1 << kRDFTerm_Description, + kRDFMask_li = 1 << kRDFTerm_li, + kRDFMask_aboutEach = 1 << kRDFTerm_aboutEach, + kRDFMask_aboutEachPrefix = 1 << kRDFTerm_aboutEachPrefix, + kRDFMask_bagID = 1 << kRDFTerm_bagID +}; + +enum { + kRDF_HasValueElem = 0x10000000UL // ! Contains rdf:value child. Must fit within kXMP_ImplReservedMask! +}; + +// ------------------------------------------------------------------------------------------------- +// GetRDFTermKind +// -------------- + +static RDFTermKind +GetRDFTermKind ( const XMP_VarString & name ) +{ + RDFTermKind term = kRDFTerm_Other; + + // Arranged to hopefully minimize the parse time for large XMP. + + if ( (name.size() > 4) && (strncmp ( name.c_str(), "rdf:", 4 ) == 0) ) { + + if ( name == "rdf:li" ) { + term = kRDFTerm_li; + } else if ( name == "rdf:parseType" ) { + term = kRDFTerm_parseType; + } else if ( name == "rdf:Description" ) { + term = kRDFTerm_Description; + } else if ( name == "rdf:about" ) { + term = kRDFTerm_about; + } else if ( name == "rdf:resource" ) { + term = kRDFTerm_resource; + } else if ( name == "rdf:RDF" ) { + term = kRDFTerm_RDF; + } else if ( name == "rdf:ID" ) { + term = kRDFTerm_ID; + } else if ( name == "rdf:nodeID" ) { + term = kRDFTerm_nodeID; + } else if ( name == "rdf:datatype" ) { + term = kRDFTerm_datatype; + } else if ( name == "rdf:aboutEach" ) { + term = kRDFTerm_aboutEach; + } else if ( name == "rdf:aboutEachPrefix" ) { + term = kRDFTerm_aboutEachPrefix; + } else if ( name == "rdf:bagID" ) { + term = kRDFTerm_bagID; + } + + } + + return term; + +} // GetRDFTermKind + +// ================================================================================================= + +static void +RemoveChild ( XMP_Node * xmpParent, size_t index ) +{ + XMP_Node * child = xmpParent->children[index]; + xmpParent->children.erase ( xmpParent->children.begin() + index ); + delete child; +} + +// ------------------------------------------------------------------------------------------------- + +static void +RemoveQualifier ( XMP_Node * xmpParent, size_t index ) +{ + XMP_Node * qualifier = xmpParent->qualifiers[index]; + xmpParent->qualifiers.erase ( xmpParent->qualifiers.begin() + index ); + delete qualifier; +} + +// ------------------------------------------------------------------------------------------------- + +static void +RemoveQualifier ( XMP_Node * xmpParent, XMP_NodePtrPos pos ) +{ + XMP_Node * qualifier = *pos; + xmpParent->qualifiers.erase ( pos ); + delete qualifier; +} + +// ================================================================================================= + +// ------------------------------------------------------------------------------------------------- +// IsCoreSyntaxTerm +// ---------------- +// +// 7.2.2 coreSyntaxTerms +// rdf:RDF | rdf:ID | rdf:about | rdf:parseType | rdf:resource | rdf:nodeID | rdf:datatype + +static bool +IsCoreSyntaxTerm ( RDFTermKind term ) +{ + if ( (kRDFTerm_FirstCore <= term) && (term <= kRDFTerm_LastCore) ) return true; + return false; +} + +// ------------------------------------------------------------------------------------------------- +// IsSyntaxTerm +// ------------ +// +// 7.2.3 syntaxTerms +// coreSyntaxTerms | rdf:Description | rdf:li + +static bool +IsSyntaxTerm ( RDFTermKind term ) +{ + if ( (kRDFTerm_FirstSyntax <= term) && (term <= kRDFTerm_LastSyntax) ) return true; + return false; +} + +// ------------------------------------------------------------------------------------------------- +// IsOldTerm +// --------- +// +// 7.2.4 oldTerms +// rdf:aboutEach | rdf:aboutEachPrefix | rdf:bagID + +static bool +IsOldTerm ( RDFTermKind term ) +{ + if ( (kRDFTerm_FirstOld <= term) && (term <= kRDFTerm_LastOld) ) return true; + return false; +} + +// ------------------------------------------------------------------------------------------------- +// IsNodeElementName +// ----------------- +// +// 7.2.5 nodeElementURIs +// anyURI - ( coreSyntaxTerms | rdf:li | oldTerms ) + +static bool +IsNodeElementName ( RDFTermKind term ) +{ + if ( (term == kRDFTerm_li) || IsOldTerm ( term ) ) return false; + return (! IsCoreSyntaxTerm ( term )); +} + +// ------------------------------------------------------------------------------------------------- +// IsPropertyElementName +// --------------------- +// +// 7.2.6 propertyElementURIs +// anyURI - ( coreSyntaxTerms | rdf:Description | oldTerms ) + +static bool +IsPropertyElementName ( RDFTermKind term ) +{ + if ( (term == kRDFTerm_Description) || IsOldTerm ( term ) ) return false; + return (! IsCoreSyntaxTerm ( term )); +} + +// ------------------------------------------------------------------------------------------------- +// IsPropertyAttributeName +// ----------------------- +// +// 7.2.7 propertyAttributeURIs +// anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms ) + +static bool +IsPropertyAttributeName ( RDFTermKind term ) +{ + if ( (term == kRDFTerm_Description) || (term == kRDFTerm_li) || IsOldTerm ( term ) ) return false; + return (! IsCoreSyntaxTerm ( term )); +} + +// ------------------------------------------------------------------------------------------------- +// IsNumberedArrayItemName +// ----------------------- +// +// Return true for a name of the form "rdf:_n", where n is a decimal integer. We're not strict about +// the integer part, it just has to be characters in the range '0'..'9'. + +static bool +IsNumberedArrayItemName ( const std::string & name ) +{ + if ( name.size() <= 5 ) return false; + if ( strncmp ( name.c_str(), "rdf:_", 5 ) != 0 ) return false; + for ( size_t i = 5; i < name.size(); ++i ) { + if ( (name[i] < '0') | (name[i] > '9') ) return false; + } + return true; +} + +// ================================================================================================= +// RDF_Parser::AddChildNode +// ======================== + +XMP_Node * RDF_Parser::AddChildNode ( XMP_Node * xmpParent, const XML_Node & xmlNode, const XMP_StringPtr value, bool isTopLevel ) +{ + + if ( xmlNode.ns.empty() ) { + XMP_Error error ( kXMPErr_BadRDF, "XML namespace required for all elements and attributes" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return 0; + } + + bool isArrayParent = (xmpParent->options & kXMP_PropValueIsArray) !=0; + bool isArrayItem = (xmlNode.name == "rdf:li"); + bool isValueNode = (xmlNode.name == "rdf:value"); + XMP_OptionBits childOptions = 0; + XMP_StringPtr childName = xmlNode.name.c_str(); + + if ( isTopLevel ) { + + // Lookup the schema node, adjust the XMP parent pointer. + XMP_Assert ( xmpParent->parent == 0 ); // Incoming parent must be the tree root. + XMP_Node * schemaNode = FindSchemaNode ( xmpParent, xmlNode.ns.c_str(), kXMP_CreateNodes ); + if ( schemaNode->options & kXMP_NewImplicitNode ) schemaNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit. + // *** Should use "opt &= ~flag" (no conditional), need runtime check for proper 32 bit code. + xmpParent = schemaNode; + + // If this is an alias set the isAlias flag in the node and the hasAliases flag in the tree. + if ( sRegisteredAliasMap->find ( xmlNode.name ) != sRegisteredAliasMap->end() ) { + childOptions |= kXMP_PropIsAlias; + schemaNode->parent->options |= kXMP_PropHasAliases; + } + + } + + // Check use of rdf:li and rdf:_n names. Must be done before calling FindChildNode! + if ( isArrayItem ) { + + // rdf:li can only be used for array children. + if ( ! isArrayParent ) { + XMP_Error error ( kXMPErr_BadRDF, "Misplaced rdf:li element" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return 0; + } + childName = kXMP_ArrayItemName; + + } else if ( isArrayParent ) { + + // Tolerate use of rdf:_n, don't verify order. + if ( IsNumberedArrayItemName ( xmlNode.name ) ) { + childName = kXMP_ArrayItemName; + isArrayItem = true; + } else { + XMP_Error error ( kXMPErr_BadRDF, "Array items cannot have arbitrary child names" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return 0; + } + + } + + // Make sure that this is not a duplicate of a named node. + if ( ! (isArrayItem | isValueNode) ) { + if ( FindChildNode ( xmpParent, childName, kXMP_ExistingOnly ) != 0 ) { + XMP_Error error ( kXMPErr_BadXMP, "Duplicate property or field node" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return 0; + } + } + + // Make sure an rdf:value node is used properly. + if ( isValueNode ) { + if ( isTopLevel || (! (xmpParent->options & kXMP_PropValueIsStruct)) ) { + XMP_Error error ( kXMPErr_BadRDF, "Misplaced rdf:value element" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return 0; + } + xmpParent->options |= kRDF_HasValueElem; + } + + // Add the new child to the XMP parent node. + XMP_Node * newChild = new XMP_Node ( xmpParent, childName, value, childOptions ); + if ( (! isValueNode) || xmpParent->children.empty() ) { + xmpParent->children.push_back ( newChild ); + } else { + xmpParent->children.insert ( xmpParent->children.begin(), newChild ); + } + + return newChild; + +} // RDF_Parser::AddChildNode + +// ================================================================================================= +// RDF_Parser::AddQualifierNode +// ============================ + +XMP_Node * RDF_Parser::AddQualifierNode ( XMP_Node * xmpParent, const XMP_VarString & name, const XMP_VarString & value ) +{ + + const bool isLang = (name == "xml:lang"); + const bool isType = (name == "rdf:type"); + + XMP_Node * newQual = 0; + + newQual = new XMP_Node ( xmpParent, name, value, kXMP_PropIsQualifier ); + + if ( ! (isLang | isType) ) { + xmpParent->qualifiers.push_back ( newQual ); + } else if ( isLang ) { + if ( xmpParent->qualifiers.empty() ) { + xmpParent->qualifiers.push_back ( newQual ); + } else { + xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), newQual ); + } + xmpParent->options |= kXMP_PropHasLang; + } else { + XMP_Assert ( isType ); + if ( xmpParent->qualifiers.empty() ) { + xmpParent->qualifiers.push_back ( newQual ); + } else { + size_t offset = 0; + if ( XMP_PropHasLang ( xmpParent->options ) ) offset = 1; + xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin()+offset, newQual ); + } + xmpParent->options |= kXMP_PropHasType; + } + + xmpParent->options |= kXMP_PropHasQualifiers; + + return newQual; + +} // RDF_Parser::AddQualifierNode + +// ================================================================================================= +// RDF_Parser::AddQualifierNode +// ============================ + +XMP_Node * RDF_Parser::AddQualifierNode ( XMP_Node * xmpParent, const XML_Node & attr ) +{ + if ( attr.ns.empty() ) { + XMP_Error error ( kXMPErr_BadRDF, "XML namespace required for all elements and attributes" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return 0; + } + + return this->AddQualifierNode ( xmpParent, attr.name, attr.value ); + +} // RDF_Parser::AddQualifierNode + +// ================================================================================================= +// RDF_Parser::FixupQualifiedNode +// ============================== +// +// The parent is an RDF pseudo-struct containing an rdf:value field. Fix the XMP data model. The +// rdf:value node must be the first child, the other children are qualifiers. The form, value, and +// children of the rdf:value node are the real ones. The rdf:value node's qualifiers must be added +// to the others. + +void RDF_Parser::FixupQualifiedNode ( XMP_Node * xmpParent ) +{ + size_t qualNum, qualLim; + size_t childNum, childLim; + + XMP_Enforce ( (xmpParent->options & kXMP_PropValueIsStruct) && (! xmpParent->children.empty()) ); + + XMP_Node * valueNode = xmpParent->children[0]; + XMP_Enforce ( valueNode->name == "rdf:value" ); + + xmpParent->qualifiers.reserve ( xmpParent->qualifiers.size() + xmpParent->children.size() + valueNode->qualifiers.size() ); + + // Move the qualifiers on the value node to the parent. Make sure an xml:lang qualifier stays at + // the front. + + qualNum = 0; + qualLim = valueNode->qualifiers.size(); + + if ( valueNode->options & kXMP_PropHasLang ) { + + if ( xmpParent->options & kXMP_PropHasLang ) { + XMP_Error error ( kXMPErr_BadXMP, "Duplicate xml:lang for rdf:value element" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + XMP_Assert ( xmpParent->qualifiers[0]->name == "xml:lang" ); + RemoveQualifier ( xmpParent, 0 ); // Use the rdf:value node's language. + } + + XMP_Node * langQual = valueNode->qualifiers[0]; + + XMP_Assert ( langQual->name == "xml:lang" ); + langQual->parent = xmpParent; + xmpParent->options |= kXMP_PropHasLang; + XMP_ClearOption ( valueNode->options, kXMP_PropHasLang ); + + if ( xmpParent->qualifiers.empty() ) { + xmpParent->qualifiers.push_back ( langQual ); // *** Should use utilities to add qual & set parent. + } else { + xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), langQual ); + } + valueNode->qualifiers[0] = 0; // We just moved it to the parent. + + qualNum = 1; // Start the remaining copy after the xml:lang qualifier. + + } + + for ( ; qualNum != qualLim; ++qualNum ) { + + XMP_Node * currQual = valueNode->qualifiers[qualNum]; + XMP_NodePtrPos existingPos; + XMP_Node * existingQual = FindQualifierNode ( xmpParent, currQual->name.c_str(), kXMP_ExistingOnly, &existingPos ); + + if ( existingQual != 0 ) { + XMP_Error error ( kXMPErr_BadXMP, "Duplicate qualifier node" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + RemoveQualifier ( xmpParent, existingPos ); // Use the rdf:value node's qualifier. + } + + currQual->parent = xmpParent; + xmpParent->qualifiers.push_back ( currQual ); + valueNode->qualifiers[qualNum] = 0; // We just moved it to the parent. + + } + + valueNode->qualifiers.clear(); // ! There should be nothing but null pointers. + + // Change the parent's other children into qualifiers. This loop starts at 1, child 0 is the + // rdf:value node. Put xml:lang at the front, append all others. + + for ( childNum = 1, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) { + + XMP_Node * currQual = xmpParent->children[childNum]; + bool isLang = (currQual->name == "xml:lang"); + + if ( FindQualifierNode ( xmpParent, currQual->name.c_str(), kXMP_ExistingOnly ) != 0 ) { + XMP_Error error ( kXMPErr_BadXMP, "Duplicate qualifier" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + delete currQual; + + } else { + + currQual->options |= kXMP_PropIsQualifier; + currQual->parent = xmpParent; + + if ( isLang ) { + xmpParent->options |= kXMP_PropHasLang; + } else if ( currQual->name == "rdf:type" ) { + xmpParent->options |= kXMP_PropHasType; + } + + if ( (! isLang) || xmpParent->qualifiers.empty() ) { + xmpParent->qualifiers.push_back ( currQual ); + } else { + xmpParent->qualifiers.insert ( xmpParent->qualifiers.begin(), currQual ); + } + + } + + xmpParent->children[childNum] = 0; // We just moved it to the qualifers, or ignored it. + + } + + if ( ! xmpParent->qualifiers.empty() ) xmpParent->options |= kXMP_PropHasQualifiers; + + // Move the options and value last, other checks need the parent's original options. Move the + // value node's children to be the parent's children. Delete the now useless value node. + + XMP_Assert ( xmpParent->options & (kXMP_PropValueIsStruct | kRDF_HasValueElem) ); + xmpParent->options &= ~ (kXMP_PropValueIsStruct | kRDF_HasValueElem); + xmpParent->options |= valueNode->options; + + xmpParent->value.swap ( valueNode->value ); + + xmpParent->children[0] = 0; // ! Remove the value node itself before the swap. + xmpParent->children.swap ( valueNode->children ); + + for ( childNum = 0, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) { + XMP_Node * currChild = xmpParent->children[childNum]; + currChild->parent = xmpParent; + } + + delete valueNode; + +} // RDF_Parser::FixupQualifiedNode + +// ================================================================================================= +// RDF_Parser::RDF +// =============== +// +// 7.2.9 RDF +// start-element ( URI == rdf:RDF, attributes == set() ) +// nodeElementList +// end-element() +// +// The top level rdf:RDF node. It can only have xmlns attributes, which have already been removed +// during construction of the XML tree. + +void RDF_Parser::RDF ( XMP_Node * xmpTree, const XML_Node & xmlNode ) +{ + + if ( ! xmlNode.attrs.empty() ) { + XMP_Error error ( kXMPErr_BadRDF, "Invalid attributes of rdf:RDF element" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + } + this->NodeElementList ( xmpTree, xmlNode, kIsTopLevel ); // ! Attributes are ignored. + +} // RDF_Parser::RDF + +// ================================================================================================= +// RDF_Parser::NodeElementList +// =========================== +// +// 7.2.10 nodeElementList +// ws* ( nodeElement ws* )* + +void RDF_Parser::NodeElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel ) +{ + XMP_Assert ( isTopLevel ); + + XML_cNodePos currChild = xmlParent.content.begin(); // *** Change these loops to the indexed pattern. + XML_cNodePos endChild = xmlParent.content.end(); + + for ( ; currChild != endChild; ++currChild ) { + if ( (*currChild)->IsWhitespaceNode() ) continue; + this->NodeElement ( xmpParent, **currChild, isTopLevel ); + } + +} // RDF_Parser::NodeElementList + +// ================================================================================================= +// RDF_Parser::NodeElement +// ======================= +// +// 7.2.5 nodeElementURIs +// anyURI - ( coreSyntaxTerms | rdf:li | oldTerms ) +// +// 7.2.11 nodeElement +// start-element ( URI == nodeElementURIs, +// attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) ) +// propertyEltList +// end-element() +// +// A node element URI is rdf:Description or anything else that is not an RDF term. + +void RDF_Parser::NodeElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ) +{ + RDFTermKind nodeTerm = GetRDFTermKind ( xmlNode.name ); + if ( (nodeTerm != kRDFTerm_Description) && (nodeTerm != kRDFTerm_Other) ) { + XMP_Error error ( kXMPErr_BadRDF, "Node element must be rdf:Description or typedNode" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + } else if ( isTopLevel && (nodeTerm == kRDFTerm_Other) ) { + XMP_Error error ( kXMPErr_BadXMP, "Top level typedNode not allowed" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + } else { + this->NodeElementAttrs ( xmpParent, xmlNode, isTopLevel ); + this->PropertyElementList ( xmpParent, xmlNode, isTopLevel ); + } + +} // RDF_Parser::NodeElement + +// ================================================================================================= +// RDF_Parser::NodeElementAttrs +// ============================ +// +// 7.2.7 propertyAttributeURIs +// anyURI - ( coreSyntaxTerms | rdf:Description | rdf:li | oldTerms ) +// +// 7.2.11 nodeElement +// start-element ( URI == nodeElementURIs, +// attributes == set ( ( idAttr | nodeIdAttr | aboutAttr )?, propertyAttr* ) ) +// propertyEltList +// end-element() +// +// Process the attribute list for an RDF node element. A property attribute URI is anything other +// than an RDF term. The rdf:ID and rdf:nodeID attributes are simply ignored, as are rdf:about +// attributes on inner nodes. + +static const XMP_OptionBits kExclusiveAttrMask = (kRDFMask_ID | kRDFMask_nodeID | kRDFMask_about); + +void RDF_Parser::NodeElementAttrs ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ) +{ + XMP_OptionBits exclusiveAttrs = 0; // Used to detect attributes that are mutually exclusive. + + XML_cNodePos currAttr = xmlNode.attrs.begin(); + XML_cNodePos endAttr = xmlNode.attrs.end(); + + for ( ; currAttr != endAttr; ++currAttr ) { + + RDFTermKind attrTerm = GetRDFTermKind ( (*currAttr)->name ); + + switch ( attrTerm ) { + + case kRDFTerm_ID : + case kRDFTerm_nodeID : + case kRDFTerm_about : + + if ( exclusiveAttrs & kExclusiveAttrMask ) { + XMP_Error error ( kXMPErr_BadRDF, "Mutally exclusive about, ID, nodeID attributes" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + continue; // Skip the later mutually exclusive attributes. + } + exclusiveAttrs |= (1 << attrTerm); + + if ( isTopLevel && (attrTerm == kRDFTerm_about) ) { + // This is the rdf:about attribute on a top level node. Set the XMP tree name if + // it doesn't have a name yet. Make sure this name matches the XMP tree name. + XMP_Assert ( xmpParent->parent == 0 ); // Must be the tree root node. + if ( xmpParent->name.empty() ) { + xmpParent->name = (*currAttr)->value; + } else if ( ! (*currAttr)->value.empty() ) { + if ( xmpParent->name != (*currAttr)->value ) { + XMP_Error error ( kXMPErr_BadXMP, "Mismatched top level rdf:about values" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + } + } + } + + break; + + case kRDFTerm_Other : + this->AddChildNode ( xmpParent, **currAttr, (*currAttr)->value.c_str(), isTopLevel ); + break; + + default : + { + XMP_Error error ( kXMPErr_BadRDF, "Invalid nodeElement attribute" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + } + continue; + + } + + } + +} // RDF_Parser::NodeElementAttrs + +// ================================================================================================= +// RDF_Parser::PropertyElementList +// =============================== +// +// 7.2.13 propertyEltList +// ws* ( propertyElt ws* )* + +void RDF_Parser::PropertyElementList ( XMP_Node * xmpParent, const XML_Node & xmlParent, bool isTopLevel ) +{ + XML_cNodePos currChild = xmlParent.content.begin(); + XML_cNodePos endChild = xmlParent.content.end(); + + for ( ; currChild != endChild; ++currChild ) { + if ( (*currChild)->IsWhitespaceNode() ) continue; + if ( (*currChild)->kind != kElemNode ) { + XMP_Error error ( kXMPErr_BadRDF, "Expected property element node not found" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + continue; + } + this->PropertyElement ( xmpParent, **currChild, isTopLevel ); + } + +} // RDF_Parser::PropertyElementList + +// ================================================================================================= +// RDF_Parser::PropertyElement +// =========================== +// +// 7.2.14 propertyElt +// resourcePropertyElt | literalPropertyElt | parseTypeLiteralPropertyElt | +// parseTypeResourcePropertyElt | parseTypeCollectionPropertyElt | parseTypeOtherPropertyElt | emptyPropertyElt +// +// 7.2.15 resourcePropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) ) +// ws* nodeElement ws* +// end-element() +// +// 7.2.16 literalPropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) ) +// text() +// end-element() +// +// 7.2.17 parseTypeLiteralPropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseLiteral ) ) +// literal +// end-element() +// +// 7.2.18 parseTypeResourcePropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) ) +// propertyEltList +// end-element() +// +// 7.2.19 parseTypeCollectionPropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseCollection ) ) +// nodeElementList +// end-element() +// +// 7.2.20 parseTypeOtherPropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) ) +// propertyEltList +// end-element() +// +// 7.2.21 emptyPropertyElt +// start-element ( URI == propertyElementURIs, +// attributes == set ( idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) ) +// end-element() +// +// The various property element forms are not distinguished by the XML element name, but by their +// attributes for the most part. The exceptions are resourcePropertyElt and literalPropertyElt. They +// are distinguished by their XML element content. +// +// NOTE: The RDF syntax does not explicitly include the xml:lang attribute although it can appear in +// many of these. We have to allow for it in the attibute counts below. + +void RDF_Parser::PropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ) +{ + RDFTermKind nodeTerm = GetRDFTermKind ( xmlNode.name ); + if ( ! IsPropertyElementName ( nodeTerm ) ) { + XMP_Error error ( kXMPErr_BadRDF, "Invalid property element name" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return; + } + + if ( xmlNode.attrs.size() > 3 ) { + + // Only an emptyPropertyElt can have more than 3 attributes. + this->EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel ); + + } else { + + // Look through the attributes for one that isn't rdf:ID or xml:lang, it will usually tell + // what we should be dealing with. The called routines must verify their specific syntax! + + XML_cNodePos currAttr = xmlNode.attrs.begin(); + XML_cNodePos endAttr = xmlNode.attrs.end(); + XMP_VarString * attrName = 0; + + for ( ; currAttr != endAttr; ++currAttr ) { + attrName = &((*currAttr)->name); + if ( (*attrName != "xml:lang") && (*attrName != "rdf:ID") ) break; + } + + if ( currAttr != endAttr ) { + + XMP_Assert ( attrName != 0 ); + XMP_VarString& attrValue = (*currAttr)->value; + + if ( *attrName == "rdf:datatype" ) { + this->LiteralPropertyElement ( xmpParent, xmlNode, isTopLevel ); + } else if ( *attrName != "rdf:parseType" ) { + this->EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel ); + } else if ( attrValue == "Literal" ) { + this->ParseTypeLiteralPropertyElement ( xmpParent, xmlNode, isTopLevel ); + } else if ( attrValue == "Resource" ) { + this->ParseTypeResourcePropertyElement ( xmpParent, xmlNode, isTopLevel ); + } else if ( attrValue == "Collection" ) { + this->ParseTypeCollectionPropertyElement ( xmpParent, xmlNode, isTopLevel ); + } else { + this->ParseTypeOtherPropertyElement ( xmpParent, xmlNode, isTopLevel ); + } + + } else { + + // Only rdf:ID and xml:lang, could be a resourcePropertyElt, a literalPropertyElt, or an. + // emptyPropertyElt. Look at the child XML nodes to decide which. + + if ( xmlNode.content.empty() ) { + + this->EmptyPropertyElement ( xmpParent, xmlNode, isTopLevel ); + + } else { + + XML_cNodePos currChild = xmlNode.content.begin(); + XML_cNodePos endChild = xmlNode.content.end(); + + for ( ; currChild != endChild; ++currChild ) { + if ( (*currChild)->kind != kCDataNode ) break; + } + + if ( currChild == endChild ) { + this->LiteralPropertyElement ( xmpParent, xmlNode, isTopLevel ); + } else { + this->ResourcePropertyElement ( xmpParent, xmlNode, isTopLevel ); + } + + } + + } + + } + +} // RDF_Parser::PropertyElement + +// ================================================================================================= +// RDF_Parser::ResourcePropertyElement +// =================================== +// +// 7.2.15 resourcePropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr? ) ) +// ws* nodeElement ws* +// end-element() +// +// This handles structs using an rdf:Description node, arrays using rdf:Bag/Seq/Alt, and Typed Nodes. +// It also catches and cleans up qualified properties written with rdf:Description and rdf:value. + +void RDF_Parser::ResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ) +{ + if ( isTopLevel && (xmlNode.name == "iX:changes") ) return; // Strip old "punchcard" chaff. + + XMP_Node * newCompound = this->AddChildNode ( xmpParent, xmlNode, "", isTopLevel ); + if ( newCompound == 0 ) return; // Ignore lower level errors. + + XML_cNodePos currAttr = xmlNode.attrs.begin(); + XML_cNodePos endAttr = xmlNode.attrs.end(); + + for ( ; currAttr != endAttr; ++currAttr ) { + XMP_VarString & attrName = (*currAttr)->name; + if ( attrName == "xml:lang" ) { + this->AddQualifierNode ( newCompound, **currAttr ); + } else if ( attrName == "rdf:ID" ) { + continue; // Ignore all rdf:ID attributes. + } else { + XMP_Error error ( kXMPErr_BadRDF, "Invalid attribute for resource property element" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + continue; + } + } + + XML_cNodePos currChild = xmlNode.content.begin(); + XML_cNodePos endChild = xmlNode.content.end(); + + for ( ; currChild != endChild; ++currChild ) { + if ( ! (*currChild)->IsWhitespaceNode() ) break; + } + if ( currChild == endChild ) { + XMP_Error error ( kXMPErr_BadRDF, "Missing child of resource property element" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return; + } + if ( (*currChild)->kind != kElemNode ) { + XMP_Error error ( kXMPErr_BadRDF, "Children of resource property element must be XML elements" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return; + } + + if ( (*currChild)->name == "rdf:Bag" ) { + newCompound->options |= kXMP_PropValueIsArray; + } else if ( (*currChild)->name == "rdf:Seq" ) { + newCompound->options |= kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered; + } else if ( (*currChild)->name == "rdf:Alt" ) { + newCompound->options |= kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate; + } else { + // This is the Typed Node case. Add an rdf:type qualifier with a URI value. + if ( (*currChild)->name != "rdf:Description" ) { + XMP_VarString typeName ( (*currChild)->ns ); + size_t colonPos = (*currChild)->name.find_first_of(':'); + if ( colonPos == XMP_VarString::npos ) { + XMP_Error error ( kXMPErr_BadXMP, "All XML elements must be in a namespace" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return; + } + typeName.append ( (*currChild)->name, colonPos+1, XMP_VarString::npos ); // Append just the local name. + XMP_Node * typeQual = this->AddQualifierNode ( newCompound, XMP_VarString("rdf:type"), typeName ); + if ( typeQual != 0 ) typeQual->options |= kXMP_PropValueIsURI; + } + newCompound->options |= kXMP_PropValueIsStruct; + } + + this->NodeElement ( newCompound, **currChild, kNotTopLevel ); + if ( newCompound->options & kRDF_HasValueElem ) { + this->FixupQualifiedNode ( newCompound ); + } else if ( newCompound->options & kXMP_PropArrayIsAlternate ) { + DetectAltText ( newCompound ); + } + + for ( ++currChild; currChild != endChild; ++currChild ) { + if ( ! (*currChild)->IsWhitespaceNode() ) { + XMP_Error error ( kXMPErr_BadRDF, "Invalid child of resource property element" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + break; // Don't bother looking for more trailing errors. + } + } + +} // RDF_Parser::ResourcePropertyElement + +// ================================================================================================= +// RDF_Parser::LiteralPropertyElement +// ================================== +// +// 7.2.16 literalPropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, datatypeAttr?) ) +// text() +// end-element() +// +// Add a leaf node with the text value and qualifiers for the attributes. + +void RDF_Parser::LiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ) +{ + XMP_Node * newChild = this->AddChildNode ( xmpParent, xmlNode, "", isTopLevel ); + if ( newChild == 0 ) return; // Ignore lower level errors. + + XML_cNodePos currAttr = xmlNode.attrs.begin(); + XML_cNodePos endAttr = xmlNode.attrs.end(); + + for ( ; currAttr != endAttr; ++currAttr ) { + XMP_VarString & attrName = (*currAttr)->name; + if ( attrName == "xml:lang" ) { + this->AddQualifierNode ( newChild, **currAttr ); + } else if ( (attrName == "rdf:ID") || (attrName == "rdf:datatype") ) { + continue; // Ignore all rdf:ID and rdf:datatype attributes. + } else { + XMP_Error error ( kXMPErr_BadRDF, "Invalid attribute for literal property element" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + continue; + } + } + + XML_cNodePos currChild = xmlNode.content.begin(); + XML_cNodePos endChild = xmlNode.content.end(); + size_t textSize = 0; + + for ( ; currChild != endChild; ++currChild ) { + if ( (*currChild)->kind == kCDataNode ) { + textSize += (*currChild)->value.size(); + } else { + XMP_Error error ( kXMPErr_BadRDF, "Invalid child of literal property element" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + } + } + + newChild->value.reserve ( textSize ); + + for ( currChild = xmlNode.content.begin(); currChild != endChild; ++currChild ) { + newChild->value += (*currChild)->value; + } + +} // RDF_Parser::LiteralPropertyElement + +// ================================================================================================= +// RDF_Parser::ParseTypeLiteralPropertyElement +// =========================================== +// +// 7.2.17 parseTypeLiteralPropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseLiteral ) ) +// literal +// end-element() + +void RDF_Parser::ParseTypeLiteralPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ) +{ + IgnoreParam(xmpParent); IgnoreParam(xmlNode); IgnoreParam(isTopLevel); + XMP_Error error ( kXMPErr_BadXMP, "ParseTypeLiteral property element not allowed" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + +} // RDF_Parser::ParseTypeLiteralPropertyElement + +// ================================================================================================= +// RDF_Parser::ParseTypeResourcePropertyElement +// ============================================ +// +// 7.2.18 parseTypeResourcePropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseResource ) ) +// propertyEltList +// end-element() +// +// Add a new struct node with a qualifier for the possible rdf:ID attribute. Then process the XML +// child nodes to get the struct fields. + +void RDF_Parser::ParseTypeResourcePropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ) +{ + XMP_Node * newStruct = this->AddChildNode ( xmpParent, xmlNode, "", isTopLevel ); + if ( newStruct == 0 ) return; // Ignore lower level errors. + newStruct->options |= kXMP_PropValueIsStruct; + + XML_cNodePos currAttr = xmlNode.attrs.begin(); + XML_cNodePos endAttr = xmlNode.attrs.end(); + + for ( ; currAttr != endAttr; ++currAttr ) { + XMP_VarString & attrName = (*currAttr)->name; + if ( attrName == "rdf:parseType" ) { + continue; // ! The caller ensured the value is "Resource". + } else if ( attrName == "xml:lang" ) { + this->AddQualifierNode ( newStruct, **currAttr ); + } else if ( attrName == "rdf:ID" ) { + continue; // Ignore all rdf:ID attributes. + } else { + XMP_Error error ( kXMPErr_BadRDF, "Invalid attribute for ParseTypeResource property element" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + continue; + } + } + + this->PropertyElementList ( newStruct, xmlNode, kNotTopLevel ); + + if ( newStruct->options & kRDF_HasValueElem ) this->FixupQualifiedNode ( newStruct ); + + // *** Need to look for arrays using rdf:Description and rdf:type. + +} // RDF_Parser::ParseTypeResourcePropertyElement + +// ================================================================================================= +// RDF_Parser::ParseTypeCollectionPropertyElement +// ============================================== +// +// 7.2.19 parseTypeCollectionPropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseCollection ) ) +// nodeElementList +// end-element() + +void RDF_Parser::ParseTypeCollectionPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ) +{ + IgnoreParam(xmpParent); IgnoreParam(xmlNode); IgnoreParam(isTopLevel); + XMP_Error error ( kXMPErr_BadXMP, "ParseTypeCollection property element not allowed" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + +} // RDF_Parser::ParseTypeCollectionPropertyElement + +// ================================================================================================= +// RDF_Parser::ParseTypeOtherPropertyElement +// ========================================= +// +// 7.2.20 parseTypeOtherPropertyElt +// start-element ( URI == propertyElementURIs, attributes == set ( idAttr?, parseOther ) ) +// propertyEltList +// end-element() + +void RDF_Parser::ParseTypeOtherPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ) +{ + IgnoreParam(xmpParent); IgnoreParam(xmlNode); IgnoreParam(isTopLevel); + XMP_Error error ( kXMPErr_BadXMP, "ParseTypeOther property element not allowed" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + +} // RDF_Parser::ParseTypeOtherPropertyElement + +// ================================================================================================= +// RDF_Parser::EmptyPropertyElement +// ================================ +// +// 7.2.21 emptyPropertyElt +// start-element ( URI == propertyElementURIs, +// attributes == set ( idAttr?, ( resourceAttr | nodeIdAttr )?, propertyAttr* ) ) +// end-element() +// +// +// +// +// +// +// An emptyPropertyElt is an element with no contained content, just a possibly empty set of +// attributes. An emptyPropertyElt can represent three special cases of simple XMP properties: a +// simple property with an empty value (ns:Prop1), a simple property whose value is a URI +// (ns:Prop2), or a simple property with simple qualifiers (ns:Prop3). An emptyPropertyElt can also +// represent an XMP struct whose fields are all simple and unqualified (ns:Prop4). +// +// It is an error to use both rdf:value and rdf:resource - that can lead to invalid RDF in the +// verbose form written using a literalPropertyElt. +// +// The XMP mapping for an emptyPropertyElt is a bit different from generic RDF, partly for +// design reasons and partly for historical reasons. The XMP mapping rules are: +// 1. If there is an rdf:value attribute then this is a simple property with a text value. +// All other attributes are qualifiers. +// 2. If there is an rdf:resource attribute then this is a simple property with a URI value. +// All other attributes are qualifiers. +// 3. If there are no attributes other than xml:lang, rdf:ID, or rdf:nodeID then this is a simple +// property with an empty value. +// 4. Otherwise this is a struct, the attributes other than xml:lang, rdf:ID, or rdf:nodeID are fields. + +void RDF_Parser::EmptyPropertyElement ( XMP_Node * xmpParent, const XML_Node & xmlNode, bool isTopLevel ) +{ + bool hasPropertyAttrs = false; + bool hasResourceAttr = false; + bool hasNodeIDAttr = false; + bool hasValueAttr = false; + + const XML_Node * valueNode = 0; // ! Can come from rdf:value or rdf:resource. + + if ( ! xmlNode.content.empty() ) { + XMP_Error error ( kXMPErr_BadRDF, "Nested content not allowed with rdf:resource or property attributes" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return; + } + + // First figure out what XMP this maps to and remember the XML node for a simple value. + + XML_cNodePos currAttr = xmlNode.attrs.begin(); + XML_cNodePos endAttr = xmlNode.attrs.end(); + + for ( ; currAttr != endAttr; ++currAttr ) { + + RDFTermKind attrTerm = GetRDFTermKind ( (*currAttr)->name ); + + switch ( attrTerm ) { + + case kRDFTerm_ID : + // Nothing to do. + break; + + case kRDFTerm_resource : + if ( hasNodeIDAttr ) { + XMP_Error error ( kXMPErr_BadRDF, "Empty property element can't have both rdf:resource and rdf:nodeID" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return; + } + if ( hasValueAttr ) { + XMP_Error error ( kXMPErr_BadXMP, "Empty property element can't have both rdf:value and rdf:resource" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return; + } + hasResourceAttr = true; + if ( ! hasValueAttr ) valueNode = *currAttr; + break; + + case kRDFTerm_nodeID : + if ( hasResourceAttr ) { + XMP_Error error ( kXMPErr_BadRDF, "Empty property element can't have both rdf:resource and rdf:nodeID" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return; + } + hasNodeIDAttr = true; + break; + + case kRDFTerm_Other : + if ( (*currAttr)->name == "rdf:value" ) { + if ( hasResourceAttr ) { + XMP_Error error ( kXMPErr_BadXMP, "Empty property element can't have both rdf:value and rdf:resource" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + return; + } + hasValueAttr = true; + valueNode = *currAttr; + } else if ( (*currAttr)->name != "xml:lang" ) { + hasPropertyAttrs = true; + } + break; + + default : + { + XMP_Error error ( kXMPErr_BadRDF, "Unrecognized attribute of empty property element" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + } + + return; + + } + + } + + // Create the right kind of child node and visit the attributes again to add the fields or qualifiers. + // ! Because of implementation vagaries, the xmpParent is the tree root for top level properties. + // ! The schema is found, created if necessary, by AddChildNode. + + XMP_Node * childNode = this->AddChildNode ( xmpParent, xmlNode, "", isTopLevel ); + if ( childNode == 0 ) return; // Ignore lower level errors. + bool childIsStruct = false; + + if ( hasValueAttr | hasResourceAttr ) { + childNode->value = valueNode->value; + if ( ! hasValueAttr ) childNode->options |= kXMP_PropValueIsURI; // ! Might have both rdf:value and rdf:resource. + } else if ( hasPropertyAttrs ) { + childNode->options |= kXMP_PropValueIsStruct; + childIsStruct = true; + } + + currAttr = xmlNode.attrs.begin(); + endAttr = xmlNode.attrs.end(); + + for ( ; currAttr != endAttr; ++currAttr ) { + + if ( *currAttr == valueNode ) continue; // Skip the rdf:value or rdf:resource attribute holding the value. + RDFTermKind attrTerm = GetRDFTermKind ( (*currAttr)->name ); + + switch ( attrTerm ) { + + case kRDFTerm_ID : + case kRDFTerm_nodeID : + break; // Ignore all rdf:ID and rdf:nodeID attributes. + + case kRDFTerm_resource : + this->AddQualifierNode ( childNode, **currAttr ); + break; + + case kRDFTerm_Other : + if ( (! childIsStruct) || (*currAttr)->name == "xml:lang" ) { + this->AddQualifierNode ( childNode, **currAttr ); + } else { + this->AddChildNode ( childNode, **currAttr, (*currAttr)->value.c_str(), false ); + } + break; + + default : + { + XMP_Error error ( kXMPErr_BadRDF, "Unrecognized attribute of empty property element" ); + this->errorCallback->NotifyClient ( kXMPErrSev_Recoverable, error ); + } + continue; + + } + + } + +} // RDF_Parser::EmptyPropertyElement + +// ================================================================================================= +// XMPMeta::ProcessRDF +// =================== +// +// Parse the XML tree of the RDF and build the corresponding XMP tree. + +void XMPMeta::ProcessRDF ( const XML_Node & rdfNode, XMP_OptionBits options ) +{ + IgnoreParam(options); + + RDF_Parser parser ( &this->errorCallback ); + + parser.RDF ( &this->tree, rdfNode ); + +} // XMPMeta::ProcessRDF + +// ================================================================================================= diff --git a/source/lib/xmp_core/UnicodeConversions.cpp b/source/lib/xmp_core/UnicodeConversions.cpp new file mode 100644 index 0000000..f9863cd --- /dev/null +++ b/source/lib/xmp_core/UnicodeConversions.cpp @@ -0,0 +1,1654 @@ +// ================================================================================================= +// Copyright 2004 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Const.h" + +#define UC_Assert(cond) /* Nothing for now, should be XMP_Assert. */ +#define UC_Throw(msg,id) throw XMP_Error ( id, msg ) + +#include "UnicodeConversions.hpp" + +#if SUNOS_SPARC || XMP_IOS_ARM + #include "string.h" +#endif + +using namespace std; + +// ================================================================================================= + +// *** Look into using asm inlines, e.g. count-leading bits for multi-byte UTF-8. + +CodePoint_to_UTF16_Proc CodePoint_to_UTF16BE = 0; +CodePoint_to_UTF16_Proc CodePoint_to_UTF16LE = 0; + +CodePoint_from_UTF16_Proc CodePoint_from_UTF16BE = 0; +CodePoint_from_UTF16_Proc CodePoint_from_UTF16LE = 0; + +UTF8_to_UTF16_Proc UTF8_to_UTF16BE = 0; +UTF8_to_UTF16_Proc UTF8_to_UTF16LE = 0; +UTF8_to_UTF32_Proc UTF8_to_UTF32BE = 0; +UTF8_to_UTF32_Proc UTF8_to_UTF32LE = 0; + +UTF16_to_UTF8_Proc UTF16BE_to_UTF8 = 0; +UTF16_to_UTF8_Proc UTF16LE_to_UTF8 = 0; +UTF32_to_UTF8_Proc UTF32BE_to_UTF8 = 0; +UTF32_to_UTF8_Proc UTF32LE_to_UTF8 = 0; + +UTF8_to_UTF16_Proc UTF8_to_UTF16Native = 0; +UTF8_to_UTF32_Proc UTF8_to_UTF32Native = 0; +UTF16_to_UTF8_Proc UTF16Native_to_UTF8 = 0; +UTF32_to_UTF8_Proc UTF32Native_to_UTF8 = 0; + +UTF16_to_UTF32_Proc UTF16BE_to_UTF32BE = 0; +UTF16_to_UTF32_Proc UTF16BE_to_UTF32LE = 0; +UTF16_to_UTF32_Proc UTF16LE_to_UTF32BE = 0; +UTF16_to_UTF32_Proc UTF16LE_to_UTF32LE = 0; + +UTF32_to_UTF16_Proc UTF32BE_to_UTF16BE = 0; +UTF32_to_UTF16_Proc UTF32BE_to_UTF16LE = 0; +UTF32_to_UTF16_Proc UTF32LE_to_UTF16BE = 0; +UTF32_to_UTF16_Proc UTF32LE_to_UTF16LE = 0; + +// ------------------------------------------------------------------------------------------------- + +static size_t swap32to16Offset = 0; // Offset to "convert" a swapped UTF32 pointer into a swapped UTF16 pointer. + +// ------------------------------------------------------------------------------------------------- + +static void CodePoint_to_UTF16Nat ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written ); +static void CodePoint_to_UTF16Swp ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written ); + +static void CodePoint_from_UTF16Nat ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read ); +static void CodePoint_from_UTF16Swp ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read ); + +// ------------------------------------------------------------------------------------------------- + +static void UTF8_to_UTF16Nat ( const UTF8Unit * utf8In, const size_t utf8Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf8Read, size_t * utf16Written ); + +static void UTF8_to_UTF16Swp ( const UTF8Unit * utf8In, const size_t utf8Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf8Read, size_t * utf16Written ); + +static void UTF8_to_UTF32Nat ( const UTF8Unit * utf8In, const size_t utf8Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf8Read, size_t * utf32Written ); + +static void UTF8_to_UTF32Swp ( const UTF8Unit * utf8In, const size_t utf8Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf8Read, size_t * utf32Written ); + +// ------------------------------------------------------------------------------------------------- + +static void UTF16Nat_to_UTF8 ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF8Unit * utf8Out, const size_t utf8Len, + size_t * utf16Read, size_t * utf8Written ); + +static void UTF16Swp_to_UTF8 ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF8Unit * utf8Out, const size_t utf8Len, + size_t * utf16Read, size_t * utf8Written ); + +static void UTF32Nat_to_UTF8 ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF8Unit * utf8Out, const size_t utf8Len, + size_t * utf32Read, size_t * utf8Written ); + +static void UTF32Swp_to_UTF8 ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF8Unit * utf8Out, const size_t utf8Len, + size_t * utf32Read, size_t * utf8Written ); + +// ------------------------------------------------------------------------------------------------- + +static void UTF16Nat_to_UTF32Nat ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf16Read, size_t * utf32Written ); + +static void UTF16Nat_to_UTF32Swp ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf16Read, size_t * utf32Written ); + +static void UTF16Swp_to_UTF32Nat ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf16Read, size_t * utf32Written ); + +static void UTF16Swp_to_UTF32Swp ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf16Read, size_t * utf32Written ); + +// ------------------------------------------------------------------------------------------------- + +static void UTF32Nat_to_UTF16Nat ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf32Read, size_t * utf16Written ); + +static void UTF32Nat_to_UTF16Swp ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf32Read, size_t * utf16Written ); + +static void UTF32Swp_to_UTF16Nat ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf32Read, size_t * utf16Written ); + +static void UTF32Swp_to_UTF16Swp ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf32Read, size_t * utf16Written ); + +// ================================================================================================= + +void InitializeUnicodeConversions() +{ + UC_Assert ( (sizeof(UTF8Unit) == 1) && (sizeof(UTF16Unit) == 2) && (sizeof(UTF32Unit) == 4) ); + + UTF16Unit u16 = 0x00FF; + bool bigEndian = (*((UTF8Unit*)&u16) == 0); + + UTF8_to_UTF16Native = UTF8_to_UTF16Nat; + UTF8_to_UTF32Native = UTF8_to_UTF32Nat; + UTF16Native_to_UTF8 = UTF16Nat_to_UTF8; + UTF32Native_to_UTF8 = UTF32Nat_to_UTF8; + + if ( bigEndian ) { + + swap32to16Offset = 0; + + CodePoint_to_UTF16BE = CodePoint_to_UTF16Nat; + CodePoint_to_UTF16LE = CodePoint_to_UTF16Swp; + + CodePoint_from_UTF16BE = CodePoint_from_UTF16Nat; + CodePoint_from_UTF16LE = CodePoint_from_UTF16Swp; + + UTF8_to_UTF16BE = UTF8_to_UTF16Nat; + UTF8_to_UTF16LE = UTF8_to_UTF16Swp; + UTF8_to_UTF32BE = UTF8_to_UTF32Nat; + UTF8_to_UTF32LE = UTF8_to_UTF32Swp; + + UTF16BE_to_UTF8 = UTF16Nat_to_UTF8; + UTF16LE_to_UTF8 = UTF16Swp_to_UTF8; + UTF32BE_to_UTF8 = UTF32Nat_to_UTF8; + UTF32LE_to_UTF8 = UTF32Swp_to_UTF8; + + UTF16BE_to_UTF32BE = UTF16Nat_to_UTF32Nat; + UTF16BE_to_UTF32LE = UTF16Nat_to_UTF32Swp; + UTF16LE_to_UTF32BE = UTF16Swp_to_UTF32Nat; + UTF16LE_to_UTF32LE = UTF16Swp_to_UTF32Swp; + + UTF32BE_to_UTF16BE = UTF32Nat_to_UTF16Nat; + UTF32BE_to_UTF16LE = UTF32Nat_to_UTF16Swp; + UTF32LE_to_UTF16BE = UTF32Swp_to_UTF16Nat; + UTF32LE_to_UTF16LE = UTF32Swp_to_UTF16Swp; + + } else { + + swap32to16Offset = 1; // ! Offset in UTF16 units! + + CodePoint_to_UTF16BE = CodePoint_to_UTF16Swp; + CodePoint_to_UTF16LE = CodePoint_to_UTF16Nat; + + CodePoint_from_UTF16BE = CodePoint_from_UTF16Swp; + CodePoint_from_UTF16LE = CodePoint_from_UTF16Nat; + + UTF8_to_UTF16BE = UTF8_to_UTF16Swp; + UTF8_to_UTF16LE = UTF8_to_UTF16Nat; + UTF8_to_UTF32BE = UTF8_to_UTF32Swp; + UTF8_to_UTF32LE = UTF8_to_UTF32Nat; + + UTF16BE_to_UTF8 = UTF16Swp_to_UTF8; + UTF16LE_to_UTF8 = UTF16Nat_to_UTF8; + UTF32BE_to_UTF8 = UTF32Swp_to_UTF8; + UTF32LE_to_UTF8 = UTF32Nat_to_UTF8; + + UTF16BE_to_UTF32BE = UTF16Swp_to_UTF32Swp; + UTF16BE_to_UTF32LE = UTF16Swp_to_UTF32Nat; + UTF16LE_to_UTF32BE = UTF16Nat_to_UTF32Swp; + UTF16LE_to_UTF32LE = UTF16Nat_to_UTF32Nat; + + UTF32BE_to_UTF16BE = UTF32Swp_to_UTF16Swp; + UTF32BE_to_UTF16LE = UTF32Swp_to_UTF16Nat; + UTF32LE_to_UTF16BE = UTF32Nat_to_UTF16Swp; + UTF32LE_to_UTF16LE = UTF32Nat_to_UTF16Nat; + + } + +} // InitializeUnicodeConversions + +// ================================================================================================= + +#if SUNOS_SPARC || XMP_IOS_ARM + #define DefineAndGetValue(type,inPtr) type inUnit; memcpy ( &inUnit, inPtr, sizeof(type) ); +#else + #define DefineAndGetValue(type,inPtr) type inUnit = *((type *)inPtr); +#endif + +static inline UTF16Unit UTF16InSwap ( const void * inPtr ) +{ + DefineAndGetValue ( UTF16Unit, inPtr ); + return (inUnit << 8) | (inUnit >> 8); +} +static inline UTF32Unit UTF32InSwap ( const void * inPtr ) +{ + DefineAndGetValue ( UTF32Unit, inPtr ); + return (inUnit << 24) | ((inUnit << 8) & 0x00FF0000) | ((inUnit >> 8) & 0x0000FF00) | (inUnit >> 24); +} + +static inline void UTF16OutSwap ( UTF16Unit * outPtr, const UTF16Unit value ) +{ + UTF16Unit outUnit = (value << 8) | (value >> 8); + *outPtr = outUnit; +} + +static inline void UTF32OutSwap ( UTF32Unit * outPtr, const UTF32Unit value ) +{ + UTF32Unit outUnit = (value << 24) | ((value << 8) & 0x00FF0000) | ((value >> 8) & 0x0000FF00) | (value >> 24); + *outPtr = outUnit; +} + +// ================================================================================================= + +void SwapUTF16 ( const UTF16Unit * utf16In, UTF16Unit * utf16Out, const size_t utf16Len ) +{ + for ( size_t i = 0; i < utf16Len; ++i ) utf16Out[i] = UTF16InSwap(utf16In+i); +} + +void SwapUTF32 ( const UTF32Unit * utf32In, UTF32Unit * utf32Out, const size_t utf32Len ) { + for ( size_t i = 0; i < utf32Len; ++i ) utf32Out[i] = UTF32InSwap(utf32In+i); +} + +// ================================================================================================= + +extern void ToUTF16 ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf16Str, bool bigEndian ) +{ + UTF8_to_UTF16_Proc Converter = UTF8_to_UTF16LE; + if ( bigEndian ) Converter = UTF8_to_UTF16BE; + + enum { kBufferSize = 8*1024 }; + UTF16Unit u16Buffer[kBufferSize]; // 16K bytes + size_t readCount, writeCount; + + utf16Str->erase(); + utf16Str->reserve ( 2*utf8Len ); // As good a guess as any. + + while ( utf8Len > 0 ) { + Converter ( utf8In, utf8Len, u16Buffer, kBufferSize, &readCount, &writeCount ); + if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML ); + utf16Str->append ( (const char *)u16Buffer, writeCount*2 ); + utf8In += readCount; + utf8Len -= readCount; + } + +} // ToUTF16 + +// ================================================================================================= + +extern void ToUTF16Native ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf16Str ) +{ + enum { kBufferSize = 8*1024 }; + UTF16Unit u16Buffer[kBufferSize]; // 16K bytes + size_t readCount, writeCount; + + utf16Str->erase(); + utf16Str->reserve ( 2*utf8Len ); // As good a guess as any. + + while ( utf8Len > 0 ) { + UTF8_to_UTF16Nat ( utf8In, utf8Len, u16Buffer, kBufferSize, &readCount, &writeCount ); + if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML ); + utf16Str->append ( (const char *)u16Buffer, writeCount*2 ); + utf8In += readCount; + utf8Len -= readCount; + } + +} // ToUTF16Native + +// ================================================================================================= + +extern void ToUTF32 ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf32Str, bool bigEndian ) +{ + UTF8_to_UTF32_Proc Converter = UTF8_to_UTF32LE; + if ( bigEndian ) Converter = UTF8_to_UTF32BE; + + enum { kBufferSize = 4*1024 }; + UTF32Unit u32Buffer[kBufferSize]; // 16K bytes + size_t readCount, writeCount; + + utf32Str->erase(); + utf32Str->reserve ( 4*utf8Len ); // As good a guess as any. + + while ( utf8Len > 0 ) { + Converter ( utf8In, utf8Len, u32Buffer, kBufferSize, &readCount, &writeCount ); + if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML ); + utf32Str->append ( (const char *)u32Buffer, writeCount*4 ); + utf8In += readCount; + utf8Len -= readCount; + } + +} // ToUTF32 + +// ================================================================================================= + +extern void ToUTF32Native ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf32Str ) +{ + enum { kBufferSize = 4*1024 }; + UTF32Unit u32Buffer[kBufferSize]; // 16K bytes + size_t readCount, writeCount; + + utf32Str->erase(); + utf32Str->reserve ( 4*utf8Len ); // As good a guess as any. + + while ( utf8Len > 0 ) { + UTF8_to_UTF32Nat ( utf8In, utf8Len, u32Buffer, kBufferSize, &readCount, &writeCount ); + if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML ); + utf32Str->append ( (const char *)u32Buffer, writeCount*4 ); + utf8In += readCount; + utf8Len -= readCount; + } + +} // ToUTF32Native + +// ================================================================================================= + +extern void FromUTF16 ( const UTF16Unit * utf16In, size_t utf16Len, std::string * utf8Str, bool bigEndian ) +{ + UTF16_to_UTF8_Proc Converter = UTF16LE_to_UTF8; + if ( bigEndian ) Converter = UTF16BE_to_UTF8; + + enum { kBufferSize = 16*1024 }; + UTF8Unit u8Buffer[kBufferSize]; + size_t readCount, writeCount; + + utf8Str->erase(); + utf8Str->reserve ( 2*utf16Len ); // As good a guess as any. + + while ( utf16Len > 0 ) { + Converter ( utf16In, utf16Len, u8Buffer, kBufferSize, &readCount, &writeCount ); + if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML ); + utf8Str->append ( (const char *)u8Buffer, writeCount ); + utf16In += readCount; + utf16Len -= readCount; + } + +} // FromUTF16 + +// ================================================================================================= + +extern void FromUTF16Native ( const UTF16Unit * utf16In, size_t utf16Len, std::string * utf8Str ) +{ + enum { kBufferSize = 16*1024 }; + UTF8Unit u8Buffer[kBufferSize]; + size_t readCount, writeCount; + + utf8Str->erase(); + utf8Str->reserve ( 2*utf16Len ); // As good a guess as any. + + while ( utf16Len > 0 ) { + UTF16Nat_to_UTF8 ( utf16In, utf16Len, u8Buffer, kBufferSize, &readCount, &writeCount ); + if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML ); + utf8Str->append ( (const char *)u8Buffer, writeCount ); + utf16In += readCount; + utf16Len -= readCount; + } + +} // FromUTF16Native + +// ================================================================================================= + +extern void FromUTF32 ( const UTF32Unit * utf32In, size_t utf32Len, std::string * utf8Str, bool bigEndian ) +{ + UTF32_to_UTF8_Proc Converter = UTF32LE_to_UTF8; + if ( bigEndian ) Converter = UTF32BE_to_UTF8; + + enum { kBufferSize = 16*1024 }; + UTF8Unit u8Buffer[kBufferSize]; + size_t readCount, writeCount; + + utf8Str->erase(); + utf8Str->reserve ( 2*utf32Len ); // As good a guess as any. + + while ( utf32Len > 0 ) { + Converter ( utf32In, utf32Len, u8Buffer, kBufferSize, &readCount, &writeCount ); + if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML ); + utf8Str->append ( (const char *)u8Buffer, writeCount ); + utf32In += readCount; + utf32Len -= readCount; + } + +} // FromUTF32 + +// ================================================================================================= + +extern void FromUTF32Native ( const UTF32Unit * utf32In, size_t utf32Len, std::string * utf8Str ) +{ + enum { kBufferSize = 16*1024 }; + UTF8Unit u8Buffer[kBufferSize]; + size_t readCount, writeCount; + + utf8Str->erase(); + utf8Str->reserve ( 2*utf32Len ); // As good a guess as any. + + while ( utf32Len > 0 ) { + UTF32Nat_to_UTF8 ( utf32In, utf32Len, u8Buffer, kBufferSize, &readCount, &writeCount ); + if ( writeCount == 0 ) UC_Throw ( "Incomplete Unicode at end of string", kXMPErr_BadXML ); + utf8Str->append ( (const char *)u8Buffer, writeCount ); + utf32In += readCount; + utf32Len -= readCount; + } + +} // FromUTF32Native + +// ================================================================================================= + +static void CodePoint_to_UTF8_Multi ( const UTF32Unit cpIn, UTF8Unit * utf8Out, const size_t utf8Len, size_t * utf8Written ) +{ + size_t unitCount = 0; + + if ( cpIn > 0x10FFFF ) UC_Throw ( "Bad UTF-32 - out of range", kXMPErr_BadParam ); + if ( (0xD800 <= cpIn) && (cpIn <= 0xDFFF) ) UC_Throw ( "Bad UTF-32 - surrogate code point", kXMPErr_BadParam ); + + // Compute the number of bytes using 6 data bits each. Then see if the highest order bits will + // fit into the leading byte. Write the UTF-8 sequence if there is enough room. + + UTF32Unit temp, mask; + size_t bytesNeeded = 0; + for ( temp = cpIn; temp != 0; temp = temp >> 6 ) ++bytesNeeded; + + temp = cpIn >> ((bytesNeeded-1)*6); // The highest order data bits. + mask = (0x80 >> bytesNeeded) - 1; // Available data bits in the leading byte. + if ( temp > mask ) ++bytesNeeded; + + if ( bytesNeeded > utf8Len ) goto Done; // Not enough room for the output. + unitCount = bytesNeeded; + + temp = cpIn; + for ( --bytesNeeded; bytesNeeded > 0; --bytesNeeded ) { + utf8Out[bytesNeeded] = 0x80 | UTF8Unit ( temp & 0x3F ); + temp = temp >> 6; + } + + mask = ~((1 << (8-unitCount)) - 1); + utf8Out[0] = UTF8Unit ( mask | temp ); + +Done: + *utf8Written = unitCount; + return; + +} // CodePoint_to_UTF8_Multi + +// ================================================================================================= + +void CodePoint_to_UTF8 ( const UTF32Unit cpIn, UTF8Unit * utf8Out, const size_t utf8Len, size_t * utf8Written ) +{ + size_t unitCount = 0; + + UC_Assert ( (utf8Out != 0) && (utf8Written != 0) ); + if ( utf8Len == 0 ) goto Done; + if ( cpIn > 0x7F ) goto MultiByte; // ! Force linear execution path for ASCII. + + unitCount = 1; + *utf8Out = UTF8Unit(cpIn); + +Done: + *utf8Written = unitCount; + return; + +MultiByte: + CodePoint_to_UTF8_Multi( cpIn, utf8Out, utf8Len, utf8Written ); + return; + +} // CodePoint_to_UTF8 + +// ================================================================================================= + +static void CodePoint_from_UTF8_Multi ( const UTF8Unit * utf8In, const size_t utf8Len, UTF32Unit * cpOut, size_t * utf8Read ) +{ + UTF8Unit inUnit = *utf8In; + size_t unitCount = 0; + UTF32Unit cp; // ! Avoid gcc complaints about declarations after goto's. + const UTF8Unit * utf8Pos; + + // ------------------------------------------------------------------------------------- + // We've got a multibyte UTF-8 character. The first byte has the number of bytes and the + // highest order data bits. The other bytes each add 6 more data bits. + + #if 0 // This might be a more effcient way to count the bytes. + static XMP_Uns8 kByteCounts[16] = { 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 2, 2, 3, 4 }; + size_t bytesNeeded = kByteCounts [ inUnit >> 4 ]; + if ( (bytesNeeded < 2) || ((bytesNeeded == 4) && ((inUnit & 0x08) != 0)) ) { + UC_Throw ( "Invalid UTF-8 sequence length", kXMPErr_BadParam ); + } + #endif + + size_t bytesNeeded = 0; // Count the leading 1 bits in the first byte. + for ( UTF8Unit temp = inUnit; temp > 0x7F; temp = temp << 1 ) ++bytesNeeded; + // *** Consider CPU-specific assembly inline, e.g. cntlzw on PowerPC. + + if ( (bytesNeeded < 2) || (bytesNeeded > 4) ) UC_Throw ( "Invalid UTF-8 sequence length", kXMPErr_BadParam ); + if ( bytesNeeded > utf8Len ) goto Done; // Not enough input in this buffer. + unitCount = bytesNeeded; + + cp = inUnit & ((1 << (7-unitCount)) - 1); // Isolate the initial data bits in the bottom of cp. + + utf8Pos = utf8In + 1; // We've absorbed the first byte. + for ( --bytesNeeded; bytesNeeded > 0; --bytesNeeded, ++utf8Pos ) { + inUnit = *utf8Pos; + if ( (inUnit & UTF8Unit(0xC0)) != UTF8Unit(0x80) ) UC_Throw ( "Invalid UTF-8 data byte", kXMPErr_BadParam ); + cp = (cp << 6) | (inUnit & 0x3F); + } + + if ( cp >= 0xD800 ) { // Skip the next comparisons most of the time. + if ( (0xD800 <= cp) && (cp <= 0xDFFF) ) UC_Throw ( "Bad UTF-8 - surrogate code point", kXMPErr_BadParam ); + if ( cp > 0x10FFFF ) UC_Throw ( "Bad UTF-8 - out of range", kXMPErr_BadParam ); + } + + *cpOut = cp; // ! Don't put after Done, don't write if no input. + +Done: + *utf8Read = unitCount; + return; + +} // CodePoint_from_UTF8_Multi + +// ================================================================================================= + +void CodePoint_from_UTF8 ( const UTF8Unit * utf8In, const size_t utf8Len, UTF32Unit * cpOut, size_t * utf8Read ) +{ + UTF8Unit inUnit; // ! Don't read until we know there is input. + size_t unitCount = 0; + + UC_Assert ( (utf8In != 0) && (cpOut != 0) && (utf8Read != 0) ); + if ( utf8Len == 0 ) goto Done; + inUnit = *utf8In; + if ( inUnit >= 0x80 ) goto MultiByte; // ! Force linear execution path for ASCII. + + unitCount = 1; + *cpOut = inUnit; // ! Don't put after Done, don't write if no input. + +Done: + *utf8Read = unitCount; + return; + +MultiByte: + CodePoint_from_UTF8_Multi ( utf8In, utf8Len, cpOut, utf8Read ); + return; + +} // CodePoint_from_UTF8 + +// ================================================================================================= + +static void CodePoint_to_UTF16Nat_Surrogate ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written ) +{ + size_t unitCount = 0; + UTF32Unit temp; // ! Avoid gcc complaints about declarations after goto's. + + if ( cpIn > 0x10FFFF ) UC_Throw ( "Bad UTF-32 - out of range", kXMPErr_BadParam ); + if ( utf16Len < 2 ) goto Done; // Not enough room for the output. + + unitCount = 2; + temp = cpIn - 0x10000; + utf16Out[0] = 0xD800 | UTF16Unit ( temp >> 10 ); + utf16Out[1] = 0xDC00 | UTF16Unit ( temp & 0x3FF ); + +Done: + *utf16Written = unitCount; + return; + +} // CodePoint_to_UTF16Nat_Surrogate + +// ================================================================================================= + +static void CodePoint_to_UTF16Nat ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written ) +{ + size_t unitCount = 0; + + UC_Assert ( (utf16Out != 0) && (utf16Written != 0) ); + if ( utf16Len == 0 ) goto Done; + if ( cpIn >= 0xD800 ) goto CheckSurrogate; // ! Force linear execution path for the BMP. + +InBMP: + unitCount = 1; + *utf16Out = UTF16Unit(cpIn); + +Done: + *utf16Written = unitCount; + return; + +CheckSurrogate: + if ( cpIn > 0xFFFF ) goto SurrogatePair; + if ( cpIn > 0xDFFF ) goto InBMP; + UC_Throw ( "Bad UTF-32 - surrogate code point", kXMPErr_BadParam ); + +SurrogatePair: + CodePoint_to_UTF16Nat_Surrogate ( cpIn, utf16Out, utf16Len, utf16Written ); + return; + +} // CodePoint_to_UTF16Nat + +// ================================================================================================= + +static void CodePoint_from_UTF16Nat_Surrogate ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read ) +{ + UTF16Unit hiUnit = *utf16In; + size_t unitCount = 0; + UTF16Unit loUnit; // ! Avoid gcc complaints about declarations after goto's. + UTF32Unit cp; + + // ---------------------------------- + // We've got a UTF-16 surrogate pair. + + if ( hiUnit > 0xDBFF ) UC_Throw ( "Bad UTF-16 - leading low surrogate", kXMPErr_BadParam ); + if ( utf16Len < 2 ) goto Done; // Not enough input in this buffer. + + loUnit = *(utf16In+1); + if ( (loUnit < 0xDC00) || (0xDFFF < loUnit) ) UC_Throw ( "Bad UTF-16 - missing low surrogate", kXMPErr_BadParam ); + + unitCount = 2; + cp = (((hiUnit & 0x3FF) << 10) | (loUnit & 0x3FF)) + 0x10000; + + *cpOut = cp; // ! Don't put after Done, don't write if no input. + +Done: + *utf16Read = unitCount; + return; + +} // CodePoint_from_UTF16Nat_Surrogate + +// ================================================================================================= + +static void CodePoint_from_UTF16Nat ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read ) +{ + UTF16Unit inUnit; // ! Don't read until we know there is input. + size_t unitCount = 0; + + UC_Assert ( (utf16In != 0) && (cpOut != 0) && (utf16Read != 0) ); + if ( utf16Len == 0 ) goto Done; + inUnit = *utf16In; + if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) goto SurrogatePair; // ! Force linear execution path for the BMP. + + unitCount = 1; + *cpOut = inUnit; // ! Don't put after Done, don't write if no input. + +Done: + *utf16Read = unitCount; + return; + +SurrogatePair: + CodePoint_from_UTF16Nat_Surrogate ( utf16In, utf16Len, cpOut, utf16Read ); + return; + +} // CodePoint_from_UTF16Nat + +// ================================================================================================= + +static void UTF8_to_UTF16Nat ( const UTF8Unit * utf8In, const size_t utf8Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf8Read, size_t * utf16Written ) +{ + const UTF8Unit * utf8Pos = utf8In; + UTF16Unit * utf16Pos = utf16Out; + + size_t utf8Left = utf8Len; + size_t utf16Left = utf16Len; + + UC_Assert ( (utf8In != 0) && (utf16Out != 0) && (utf8Read != 0) && (utf16Written != 0) ); + + while ( (utf8Left > 0) && (utf16Left > 0) ) { + + // Do a run of ASCII, it copies 1 input unit into 1 output unit. + size_t i, limit = utf8Left; + if ( limit > utf16Left ) limit = utf16Left; + for ( i = 0; i < limit; ++i ) { + UTF8Unit inUnit = *utf8Pos; + if ( inUnit > 0x7F ) break; + *utf16Pos = inUnit; + ++utf8Pos; + ++utf16Pos; + } + utf8Left -= i; + utf16Left -= i; + + // Do a run of non-ASCII, it copies multiple input units into 1 or 2 output units. + while ( (utf8Left > 0) && (utf16Left > 0) ) { + UTF32Unit cp; + size_t len8, len16; + UTF8Unit inUnit = *utf8Pos; + if ( inUnit <= 0x7F ) break; + CodePoint_from_UTF8_Multi ( utf8Pos, utf8Left, &cp, &len8 ); + if ( len8 == 0 ) goto Done; // The input buffer ends in the middle of a character. + if ( cp <= 0xFFFF ) { + *utf16Pos = UTF16Unit(cp); + len16 = 1; + } else { + CodePoint_to_UTF16Nat_Surrogate ( cp, utf16Pos, utf16Left, &len16 ); + if ( len16 == 0 ) goto Done; // Not enough room in the output buffer. + } + utf8Left -= len8; + utf8Pos += len8; + utf16Left -= len16; + utf16Pos += len16; + } + + } + +Done: // Set the output lengths. + *utf8Read = utf8Len - utf8Left; + *utf16Written = utf16Len - utf16Left; + +} // UTF8_to_UTF16Nat + +// ================================================================================================= + +static void UTF8_to_UTF32Nat ( const UTF8Unit * utf8In, const size_t utf8Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf8Read, size_t * utf32Written ) +{ + const UTF8Unit * utf8Pos = utf8In; + UTF32Unit * utf32Pos = utf32Out; + + size_t utf8Left = utf8Len; + size_t utf32Left = utf32Len; + + UC_Assert ( (utf8In != 0) && (utf32Out != 0) && (utf8Read != 0) && (utf32Written != 0) ); + + while ( (utf8Left > 0) && (utf32Left > 0) ) { + + // Do a run of ASCII, it copies 1 input unit into 1 output unit. + size_t i, limit = utf8Left; + if ( limit > utf32Left ) limit = utf32Left; + for ( i = 0; i < limit; ++i ) { + UTF8Unit inUnit = *utf8Pos; + if ( inUnit > 0x7F ) break; + *utf32Pos = inUnit; + ++utf8Pos; + ++utf32Pos; + } + utf8Left -= i; + utf32Left -= i; + + // Do a run of non-ASCII, it copies variable input into 1 output unit. + while ( (utf8Left > 0) && (utf32Left > 0) ) { + size_t len; + UTF8Unit inUnit = *utf8Pos; + if ( inUnit <= 0x7F ) break; + CodePoint_from_UTF8_Multi ( utf8Pos, utf8Left, utf32Pos, &len ); + if ( len == 0 ) goto Done; // The input buffer ends in the middle of a character. + utf8Left -= len; + utf8Pos += len; + utf32Left -= 1; + utf32Pos += 1; + } + + } + +Done: // Set the output lengths. + *utf8Read = utf8Len - utf8Left; + *utf32Written = utf32Len - utf32Left; + +} // UTF8_to_UTF32Nat + +// ================================================================================================= + +static void UTF16Nat_to_UTF8 ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF8Unit * utf8Out, const size_t utf8Len, + size_t * utf16Read, size_t * utf8Written ) +{ + const UTF16Unit * utf16Pos = utf16In; + UTF8Unit * utf8Pos = utf8Out; + + size_t utf16Left = utf16Len; + size_t utf8Left = utf8Len; + + UC_Assert ( (utf16In != 0) && (utf8Out != 0) && (utf16Read != 0) && (utf8Written != 0) ); + + while ( (utf16Left > 0) && (utf8Left > 0) ) { + + // Do a run of ASCII, it copies 1 input unit into 1 output unit. + size_t i, limit = utf16Left; + if ( limit > utf8Left ) limit = utf8Left; + for ( i = 0; i < limit; ++i ) { + UTF16Unit inUnit = *utf16Pos; + if ( inUnit > 0x7F ) break; + *utf8Pos = UTF8Unit(inUnit); + ++utf16Pos; + ++utf8Pos; + } + utf16Left -= i; + utf8Left -= i; + + // Do a run of non-ASCII inside the BMP, it copies 1 input unit into multiple output units. + while ( (utf16Left > 0) && (utf8Left > 0) ) { + size_t len8; + UTF16Unit inUnit = *utf16Pos; + if ( inUnit <= 0x7F ) break; + if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) break; + CodePoint_to_UTF8_Multi ( inUnit, utf8Pos, utf8Left, &len8 ); + if ( len8 == 0 ) goto Done; // Not enough room in the output buffer. + utf16Left -= 1; + utf16Pos += 1; + utf8Left -= len8; + utf8Pos += len8; + } + + // Do a run of surrogate pairs, it copies 2 input units into multiple output units. + while ( (utf16Left > 0) && (utf8Left > 0) ) { + UTF32Unit cp; + size_t len16, len8; + UTF16Unit inUnit = *utf16Pos; + if ( (inUnit < 0xD800) || (0xDFFF < inUnit) ) break; + CodePoint_from_UTF16Nat_Surrogate ( utf16Pos, utf16Left, &cp, &len16 ); + if ( len16 == 0 ) goto Done; // The input buffer ends in the middle of a surrogate pair. + UC_Assert ( len16 == 2 ); + CodePoint_to_UTF8_Multi ( cp, utf8Pos, utf8Left, &len8 ); + if ( len8 == 0 ) goto Done; // Not enough room in the output buffer. + utf16Left -= len16; + utf16Pos += len16; + utf8Left -= len8; + utf8Pos += len8; + } + + } + +Done: // Set the output lengths. + *utf16Read = utf16Len - utf16Left; + *utf8Written = utf8Len - utf8Left; + +} // UTF16Nat_to_UTF8 + +// ================================================================================================= + +static void UTF32Nat_to_UTF8 ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF8Unit * utf8Out, const size_t utf8Len, + size_t * utf32Read, size_t * utf8Written ) +{ + const UTF32Unit * utf32Pos = utf32In; + UTF8Unit * utf8Pos = utf8Out; + + size_t utf32Left = utf32Len; + size_t utf8Left = utf8Len; + + UC_Assert ( (utf32In != 0) && (utf8Out != 0) && (utf32Read != 0) && (utf8Written != 0) ); + + while ( (utf32Left > 0) && (utf8Left > 0) ) { + + // Do a run of ASCII, it copies 1 input unit into 1 output unit. + size_t i, limit = utf32Left; + if ( limit > utf8Left ) limit = utf8Left; + for ( i = 0; i < limit; ++i ) { + UTF32Unit inUnit = *utf32Pos; + if ( inUnit > 0x7F ) break; + *utf8Pos = UTF8Unit(inUnit); + ++utf32Pos; + ++utf8Pos; + } + utf32Left -= i; + utf8Left -= i; + + // Do a run of non-ASCII, it copies 1 input unit into multiple output units. + while ( (utf32Left > 0) && (utf8Left > 0) ) { + size_t len; + UTF32Unit inUnit = *utf32Pos; + if ( inUnit <= 0x7F ) break; + CodePoint_to_UTF8_Multi ( inUnit, utf8Pos, utf8Left, &len ); + if ( len == 0 ) goto Done; // Not enough room in the output buffer. + utf32Left -= 1; + utf32Pos += 1; + utf8Left -= len; + utf8Pos += len; + } + + } + +Done: // Set the output lengths. + *utf32Read = utf32Len - utf32Left; + *utf8Written = utf8Len - utf8Left; + +} // UTF32Nat_to_UTF8 + +// ================================================================================================= + +static void UTF16Nat_to_UTF32Nat ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf16Read, size_t * utf32Written ) +{ + const UTF16Unit * utf16Pos = utf16In; + UTF32Unit * utf32Pos = utf32Out; + + size_t utf16Left = utf16Len; + size_t utf32Left = utf32Len; + + UC_Assert ( (utf16In != 0) && (utf32Out != 0) && (utf16Read != 0) && (utf32Written != 0) ); + + while ( (utf16Left > 0) && (utf32Left > 0) ) { + + // Do a run of BMP, it copies 1 input unit into 1 output unit. + size_t i, limit = utf16Left; + if ( limit > utf32Left ) limit = utf32Left; + for ( i = 0; i < limit; ++i ) { + UTF16Unit inUnit = *utf16Pos; + if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) break; + *utf32Pos = inUnit; + ++utf16Pos; + ++utf32Pos; + } + utf16Left -= i; + utf32Left -= i; + + // Do a run of surrogate pairs, it copies 2 input units into 1 output unit. + while ( (utf16Left > 0) && (utf32Left > 0) ) { + size_t len; + UTF16Unit inUnit = *utf16Pos; + if ( (inUnit < 0xD800) || (0xDFFF < inUnit) ) break; + CodePoint_from_UTF16Nat_Surrogate ( utf16Pos, utf16Left, utf32Pos, &len ); + if ( len == 0 ) goto Done; // The input buffer ends in the middle of a surrogate pair. + UC_Assert ( len == 2 ); + utf16Left -= len; + utf16Pos += len; + utf32Left -= 1; + utf32Pos += 1; + } + + } + +Done: // Set the output lengths. + *utf16Read = utf16Len - utf16Left; + *utf32Written = utf32Len - utf32Left; + +} // UTF16Nat_to_UTF32Nat + +// ================================================================================================= + +static void UTF32Nat_to_UTF16Nat ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf32Read, size_t * utf16Written ) +{ + const UTF32Unit * utf32Pos = utf32In; + UTF16Unit * utf16Pos = utf16Out; + + size_t utf32Left = utf32Len; + size_t utf16Left = utf16Len; + + UC_Assert ( (utf32In != 0) && (utf16Out != 0) && (utf32Read != 0) && (utf16Written != 0) ); + + while ( (utf32Left > 0) && (utf16Left > 0) ) { + + // Do a run of BMP, it copies 1 input unit into 1 output unit. + size_t i, limit = utf32Left; + if ( limit > utf16Left ) limit = utf16Left; + for ( i = 0; i < limit; ++i ) { + UTF32Unit inUnit = *utf32Pos; + if ( inUnit > 0xFFFF ) break; + *utf16Pos = UTF16Unit(inUnit); + ++utf32Pos; + ++utf16Pos; + } + utf32Left -= i; + utf16Left -= i; + + // Do a run of non-BMP, it copies 1 input unit into 2 output units. + while ( (utf32Left > 0) && (utf16Left > 0) ) { + size_t len; + UTF32Unit inUnit = *utf32Pos; + if ( inUnit <= 0xFFFF ) break; + CodePoint_to_UTF16Nat_Surrogate ( inUnit, utf16Pos, utf16Left, &len ); + if ( len == 0 ) goto Done; // Not enough room in the output buffer. + UC_Assert ( len == 2 ); + utf32Left -= 1; + utf32Pos += 1; + utf16Left -= 2; + utf16Pos += 2; + } + + } + +Done: // Set the output lengths. + *utf32Read = utf32Len - utf32Left; + *utf16Written = utf16Len - utf16Left; + +} // UTF32Nat_to_UTF16Nat + +// ================================================================================================= + +static void CodePoint_to_UTF16Swp_Surrogate ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written ) +{ + size_t unitCount = 0; + UTF32Unit temp; // ! Avoid gcc complaints about declarations after goto's. + + if ( cpIn > 0x10FFFF ) UC_Throw ( "Bad UTF-32 - out of range", kXMPErr_BadParam ); + if ( utf16Len < 2 ) goto Done; // Not enough room for the output. + + unitCount = 2; + temp = cpIn - 0x10000; + UTF16OutSwap ( &utf16Out[0], (0xD800 | UTF16Unit ( temp >> 10 )) ); + UTF16OutSwap ( &utf16Out[1], (0xDC00 | UTF16Unit ( temp & 0x3FF)) ); + +Done: + *utf16Written = unitCount; + return; + +} // CodePoint_to_UTF16Swp_Surrogate + +// ================================================================================================= + +static void CodePoint_to_UTF16Swp ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written ) +{ + size_t unitCount = 0; + + UC_Assert ( (utf16Out != 0) && (utf16Written != 0) ); + if ( utf16Len == 0 ) goto Done; + if ( cpIn >= 0xD800 ) goto CheckSurrogate; // ! Force linear execution path for the BMP. + +InBMP: + unitCount = 1; + UTF16OutSwap ( utf16Out, UTF16Unit(cpIn) ); + +Done: + *utf16Written = unitCount; + return; + +CheckSurrogate: + if ( cpIn > 0xFFFF ) goto SurrogatePair; + if ( cpIn > 0xDFFF ) goto InBMP; + UC_Throw ( "Bad UTF-32 - surrogate code point", kXMPErr_BadParam ); + +SurrogatePair: + CodePoint_to_UTF16Swp_Surrogate ( cpIn, utf16Out, utf16Len, utf16Written ); + return; + +} // CodePoint_to_UTF16Swp + +// ================================================================================================= + +static void CodePoint_from_UTF16Swp_Surrogate ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read ) +{ + UTF16Unit hiUnit = UTF16InSwap(utf16In); + size_t unitCount = 0; + UTF16Unit loUnit; // ! Avoid gcc complaints about declarations after goto's. + UTF32Unit cp; + + // ---------------------------------- + // We've got a UTF-16 surrogate pair. + + if ( hiUnit > 0xDBFF ) UC_Throw ( "Bad UTF-16 - leading low surrogate", kXMPErr_BadParam ); + if ( utf16Len < 2 ) goto Done; // Not enough input in this buffer. + + loUnit = UTF16InSwap(utf16In+1); + if ( (loUnit < 0xDC00) || (0xDFFF < loUnit) ) UC_Throw ( "Bad UTF-16 - missing low surrogate", kXMPErr_BadParam ); + + unitCount = 2; + cp = (((hiUnit & 0x3FF) << 10) | (loUnit & 0x3FF)) + 0x10000; + + *cpOut = cp; // ! Don't put after Done, don't write if no input. + +Done: + *utf16Read = unitCount; + return; + +} // CodePoint_from_UTF16Swp_Surrogate + +// ================================================================================================= + +static void CodePoint_from_UTF16Swp ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read ) +{ + UTF16Unit inUnit; // ! Don't read until we know there is input. + size_t unitCount = 0; + + UC_Assert ( (utf16In != 0) && (cpOut != 0) && (utf16Read != 0) ); + if ( utf16Len == 0 ) goto Done; + inUnit = UTF16InSwap(utf16In); + if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) goto SurrogatePair; // ! Force linear execution path for the BMP. + + unitCount = 1; + *cpOut = inUnit; // ! Don't put after Done, don't write if no input. + +Done: + *utf16Read = unitCount; + return; + +SurrogatePair: + CodePoint_from_UTF16Swp_Surrogate ( utf16In, utf16Len, cpOut, utf16Read ); + return; + +} // CodePoint_from_UTF16Swp + +// ================================================================================================= + +static void UTF8_to_UTF16Swp ( const UTF8Unit * utf8In, const size_t utf8Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf8Read, size_t * utf16Written ) +{ + const UTF8Unit * utf8Pos = utf8In; + UTF16Unit * utf16Pos = utf16Out; + + size_t utf8Left = utf8Len; + size_t utf16Left = utf16Len; + + UC_Assert ( (utf8In != 0) && (utf16Out != 0) && (utf8Read != 0) && (utf16Written != 0) ); + + while ( (utf8Left > 0) && (utf16Left > 0) ) { + + // Do a run of ASCII, it copies 1 input unit into 1 output unit. + size_t i, limit = utf8Left; + if ( limit > utf16Left ) limit = utf16Left; + for ( i = 0; i < limit; ++i ) { + UTF8Unit inUnit = *utf8Pos; + if ( inUnit > 0x7F ) break; + *utf16Pos = UTF16Unit(inUnit) << 8; // Better than: UTF16OutSwap ( utf16Pos, inUnit ); + ++utf8Pos; + ++utf16Pos; + } + utf8Left -= i; + utf16Left -= i; + + // Do a run of non-ASCII, it copies multiple input units into 1 or 2 output units. + while ( (utf8Left > 0) && (utf16Left > 0) ) { + UTF32Unit cp; + size_t len8, len16; + UTF8Unit inUnit = *utf8Pos; + if ( inUnit <= 0x7F ) break; + CodePoint_from_UTF8_Multi ( utf8Pos, utf8Left, &cp, &len8 ); + if ( len8 == 0 ) goto Done; // The input buffer ends in the middle of a character. + if ( cp <= 0xFFFF ) { + UTF16OutSwap ( utf16Pos, UTF16Unit(cp) ); + len16 = 1; + } else { + CodePoint_to_UTF16Swp_Surrogate ( cp, utf16Pos, utf16Left, &len16 ); + if ( len16 == 0 ) goto Done; // Not enough room in the output buffer. + } + utf8Left -= len8; + utf8Pos += len8; + utf16Left -= len16; + utf16Pos += len16; + } + + } + +Done: // Set the output lengths. + *utf8Read = utf8Len - utf8Left; + *utf16Written = utf16Len - utf16Left; + +} // UTF8_to_UTF16Swp + +// ================================================================================================= + +static void UTF8_to_UTF32Swp ( const UTF8Unit * utf8In, const size_t utf8Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf8Read, size_t * utf32Written ) +{ + const UTF8Unit * utf8Pos = utf8In; + UTF32Unit * utf32Pos = utf32Out; + + size_t utf8Left = utf8Len; + size_t utf32Left = utf32Len; + + UC_Assert ( (utf8In != 0) && (utf32Out != 0) && (utf8Read != 0) && (utf32Written != 0) ); + + while ( (utf8Left > 0) && (utf32Left > 0) ) { + + // Do a run of ASCII, it copies 1 input unit into 1 output unit. + size_t i, limit = utf8Left; + if ( limit > utf32Left ) limit = utf32Left; + for ( i = 0; i < limit; ++i ) { + UTF8Unit inUnit = *utf8Pos; + if ( inUnit > 0x7F ) break; + *utf32Pos = UTF32Unit(inUnit) << 24; // Better than: UTF32OutSwap ( utf32Pos, inUnit ); + ++utf8Pos; + ++utf32Pos; + } + utf8Left -= i; + utf32Left -= i; + + // Do a run of non-ASCII, it copies variable input into 1 output unit. + while ( (utf8Left > 0) && (utf32Left > 0) ) { + size_t len; + UTF32Unit cp; + UTF8Unit inUnit = *utf8Pos; + if ( inUnit <= 0x7F ) break; + CodePoint_from_UTF8_Multi ( utf8Pos, utf8Left, &cp, &len ); + if ( len == 0 ) goto Done; // The input buffer ends in the middle of a character. + UTF32OutSwap ( utf32Pos, cp ); + utf8Left -= len; + utf8Pos += len; + utf32Left -= 1; + utf32Pos += 1; + } + + } + +Done: // Set the output lengths. + *utf8Read = utf8Len - utf8Left; + *utf32Written = utf32Len - utf32Left; + +} // UTF8_to_UTF32Swp + +// ================================================================================================= + +static void UTF16Swp_to_UTF8 ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF8Unit * utf8Out, const size_t utf8Len, + size_t * utf16Read, size_t * utf8Written ) +{ + const UTF16Unit * utf16Pos = utf16In; + UTF8Unit * utf8Pos = utf8Out; + + size_t utf16Left = utf16Len; + size_t utf8Left = utf8Len; + + UC_Assert ( (utf16In != 0) && (utf8Out != 0) && (utf16Read != 0) && (utf8Written != 0) ); + + while ( (utf16Left > 0) && (utf8Left > 0) ) { + + // Do a run of ASCII, it copies 1 input unit into 1 output unit. + size_t i, limit = utf16Left; + if ( limit > utf8Left ) limit = utf8Left; + for ( i = 0; i < limit; ++i ) { + UTF16Unit inUnit = UTF16InSwap(utf16Pos); + if ( inUnit > 0x7F ) break; + *utf8Pos = UTF8Unit(inUnit); + ++utf16Pos; + ++utf8Pos; + } + utf16Left -= i; + utf8Left -= i; + + // Do a run of non-ASCII inside the BMP, it copies 1 input unit into multiple output units. + while ( (utf16Left > 0) && (utf8Left > 0) ) { + size_t len8; + UTF16Unit inUnit = UTF16InSwap(utf16Pos); + if ( inUnit <= 0x7F ) break; + if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) break; + CodePoint_to_UTF8_Multi ( inUnit, utf8Pos, utf8Left, &len8 ); + if ( len8 == 0 ) goto Done; // Not enough room in the output buffer. + utf16Left -= 1; + utf16Pos += 1; + utf8Left -= len8; + utf8Pos += len8; + } + + // Do a run of surrogate pairs, it copies 2 input units into multiple output units. + while ( (utf16Left > 0) && (utf8Left > 0) ) { + UTF32Unit cp; + size_t len16, len8; + UTF16Unit inUnit = UTF16InSwap(utf16Pos); + if ( (inUnit < 0xD800) || (0xDFFF < inUnit) ) break; + CodePoint_from_UTF16Swp_Surrogate ( utf16Pos, utf16Left, &cp, &len16 ); + if ( len16 == 0 ) goto Done; // The input buffer ends in the middle of a surrogate pair. + UC_Assert ( len16 == 2 ); + CodePoint_to_UTF8_Multi ( cp, utf8Pos, utf8Left, &len8 ); + if ( len8 == 0 ) goto Done; // Not enough room in the output buffer. + utf16Left -= len16; + utf16Pos += len16; + utf8Left -= len8; + utf8Pos += len8; + } + + } + +Done: // Set the output lengths. + *utf16Read = utf16Len - utf16Left; + *utf8Written = utf8Len - utf8Left; + +} // UTF16Swp_to_UTF8 + +// ================================================================================================= + +static void UTF32Swp_to_UTF8 ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF8Unit * utf8Out, const size_t utf8Len, + size_t * utf32Read, size_t * utf8Written ) +{ + const UTF32Unit * utf32Pos = utf32In; + UTF8Unit * utf8Pos = utf8Out; + + size_t utf32Left = utf32Len; + size_t utf8Left = utf8Len; + + UC_Assert ( (utf32In != 0) && (utf8Out != 0) && (utf32Read != 0) && (utf8Written != 0) ); + + while ( (utf32Left > 0) && (utf8Left > 0) ) { + + // Do a run of ASCII, it copies 1 input unit into 1 output unit. + size_t i, limit = utf32Left; + if ( limit > utf8Left ) limit = utf8Left; + for ( i = 0; i < limit; ++i ) { + UTF32Unit cp = UTF32InSwap(utf32Pos); + if ( cp > 0x7F ) break; + *utf8Pos = UTF8Unit(cp); + ++utf32Pos; + ++utf8Pos; + } + utf32Left -= i; + utf8Left -= i; + + // Do a run of non-ASCII, it copies 1 input unit into multiple output units. + while ( (utf32Left > 0) && (utf8Left > 0) ) { + size_t len; + UTF32Unit cp = UTF32InSwap(utf32Pos); + if ( cp <= 0x7F ) break; + CodePoint_to_UTF8_Multi ( cp, utf8Pos, utf8Left, &len ); + if ( len == 0 ) goto Done; // Not enough room in the output buffer. + utf32Left -= 1; + utf32Pos += 1; + utf8Left -= len; + utf8Pos += len; + } + + } + +Done: // Set the output lengths. + *utf32Read = utf32Len - utf32Left; + *utf8Written = utf8Len - utf8Left; + +} // UTF32Swp_to_UTF8 + +// ================================================================================================= + +static void UTF16Swp_to_UTF32Swp ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf16Read, size_t * utf32Written ) +{ + const UTF16Unit * utf16Pos = utf16In; + UTF32Unit * utf32Pos = utf32Out; + + size_t utf16Left = utf16Len; + size_t utf32Left = utf32Len; + + UC_Assert ( (utf16In != 0) && (utf32Out != 0) && (utf16Read != 0) && (utf32Written != 0) ); + + while ( (utf16Left > 0) && (utf32Left > 0) ) { + + // Do a run of BMP, it copies 1 input unit into 1 output unit. + size_t i, limit = utf16Left; + if ( limit > utf32Left ) limit = utf32Left; + for ( i = 0; i < limit; ++i ) { + UTF16Unit inUnit = UTF16InSwap(utf16Pos); + if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) break; + *utf32Pos = UTF32Unit(*utf16Pos) << 16; // Better than: UTF32OutSwap ( utf32Pos, inUnit ); + ++utf16Pos; + ++utf32Pos; + } + utf16Left -= i; + utf32Left -= i; + + // Do a run of surrogate pairs, it copies 2 input units into 1 output unit. + while ( (utf16Left > 0) && (utf32Left > 0) ) { + size_t len; + UTF32Unit cp; + UTF16Unit inUnit = UTF16InSwap(utf16Pos); + if ( (inUnit < 0xD800) || (0xDFFF < inUnit) ) break; + CodePoint_from_UTF16Swp_Surrogate ( utf16Pos, utf16Left, &cp, &len ); + if ( len == 0 ) goto Done; // The input buffer ends in the middle of a surrogate pair. + UTF32OutSwap ( utf32Pos, cp ); + UC_Assert ( len == 2 ); + utf16Left -= len; + utf16Pos += len; + utf32Left -= 1; + utf32Pos += 1; + } + + } + +Done: // Set the output lengths. + *utf16Read = utf16Len - utf16Left; + *utf32Written = utf32Len - utf32Left; + +} // UTF16Swp_to_UTF32Swp + +// ================================================================================================= + +static void UTF32Swp_to_UTF16Swp ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf32Read, size_t * utf16Written ) +{ + const UTF32Unit * utf32Pos = utf32In; + UTF16Unit * utf16Pos = utf16Out; + + size_t utf32Left = utf32Len; + size_t utf16Left = utf16Len; + + const size_t k32to16Offset = swap32to16Offset; // ! Make sure compiler treats as an invariant. + + UC_Assert ( (utf32In != 0) && (utf16Out != 0) && (utf32Read != 0) && (utf16Written != 0) ); + + while ( (utf32Left > 0) && (utf16Left > 0) ) { + + // Do a run of BMP, it copies 1 input unit into 1 output unit. + size_t i, limit = utf32Left; + if ( limit > utf16Left ) limit = utf16Left; + for ( i = 0; i < limit; ++i ) { + UTF32Unit inUnit = UTF32InSwap(utf32Pos); + if ( inUnit > 0xFFFF ) break; + *utf16Pos = *(((UTF16Unit*)utf32Pos) + k32to16Offset); // Better than: UTF16OutSwap ( utf16Pos, UTF16Unit(inUnit) ); + ++utf32Pos; + ++utf16Pos; + } + utf32Left -= i; + utf16Left -= i; + + // Do a run of non-BMP, it copies 1 input unit into 2 output units. + while ( (utf32Left > 0) && (utf16Left > 0) ) { + size_t len; + UTF32Unit inUnit = UTF32InSwap(utf32Pos); + if ( inUnit <= 0xFFFF ) break; + CodePoint_to_UTF16Swp_Surrogate ( inUnit, utf16Pos, utf16Left, &len ); + if ( len == 0 ) goto Done; // Not enough room in the output buffer. + UC_Assert ( len == 2 ); + utf32Left -= 1; + utf32Pos += 1; + utf16Left -= 2; + utf16Pos += 2; + } + + } + +Done: // Set the output lengths. + *utf32Read = utf32Len - utf32Left; + *utf16Written = utf16Len - utf16Left; + +} // UTF32Swp_to_UTF16Swp + +// ================================================================================================= + +static void UTF16Nat_to_UTF32Swp ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf16Read, size_t * utf32Written ) +{ + const UTF16Unit * utf16Pos = utf16In; + UTF32Unit * utf32Pos = utf32Out; + + size_t utf16Left = utf16Len; + size_t utf32Left = utf32Len; + + UC_Assert ( (utf16In != 0) && (utf32Out != 0) && (utf16Read != 0) && (utf32Written != 0) ); + + while ( (utf16Left > 0) && (utf32Left > 0) ) { + + // Do a run of BMP, it copies 1 input unit into 1 output unit. + size_t i, limit = utf16Left; + if ( limit > utf32Left ) limit = utf32Left; + for ( i = 0; i < limit; ++i ) { + UTF16Unit inUnit = *utf16Pos; + if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) break; + UTF32OutSwap ( utf32Pos, inUnit ); + ++utf16Pos; + ++utf32Pos; + } + utf16Left -= i; + utf32Left -= i; + + // Do a run of surrogate pairs, it copies 2 input units into 1 output unit. + while ( (utf16Left > 0) && (utf32Left > 0) ) { + size_t len; + UTF32Unit cp; + UTF16Unit inUnit = *utf16Pos; + if ( (inUnit < 0xD800) || (0xDFFF < inUnit) ) break; + CodePoint_from_UTF16Nat_Surrogate ( utf16Pos, utf16Left, &cp, &len ); + if ( len == 0 ) goto Done; // The input buffer ends in the middle of a surrogate pair. + UC_Assert ( len == 2 ); + UTF32OutSwap ( utf32Pos, cp ); + utf16Left -= len; + utf16Pos += len; + utf32Left -= 1; + utf32Pos += 1; + } + + } + +Done: // Set the output lengths. + *utf16Read = utf16Len - utf16Left; + *utf32Written = utf32Len - utf32Left; + +} // UTF16Nat_to_UTF32Swp + +// ================================================================================================= + +static void UTF16Swp_to_UTF32Nat ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf16Read, size_t * utf32Written ) +{ + const UTF16Unit * utf16Pos = utf16In; + UTF32Unit * utf32Pos = utf32Out; + + size_t utf16Left = utf16Len; + size_t utf32Left = utf32Len; + + UC_Assert ( (utf16In != 0) && (utf32Out != 0) && (utf16Read != 0) && (utf32Written != 0) ); + + while ( (utf16Left > 0) && (utf32Left > 0) ) { + + // Do a run of BMP, it copies 1 input unit into 1 output unit. + size_t i, limit = utf16Left; + if ( limit > utf32Left ) limit = utf32Left; + for ( i = 0; i < limit; ++i ) { + UTF16Unit inUnit = UTF16InSwap(utf16Pos); + if ( (0xD800 <= inUnit) && (inUnit <= 0xDFFF) ) break; + *utf32Pos = inUnit; + ++utf16Pos; + ++utf32Pos; + } + utf16Left -= i; + utf32Left -= i; + + // Do a run of surrogate pairs, it copies 2 input units into 1 output unit. + while ( (utf16Left > 0) && (utf32Left > 0) ) { + size_t len; + UTF16Unit inUnit = UTF16InSwap(utf16Pos); + if ( (inUnit < 0xD800) || (0xDFFF < inUnit) ) break; + CodePoint_from_UTF16Swp_Surrogate ( utf16Pos, utf16Left, utf32Pos, &len ); + if ( len == 0 ) goto Done; // The input buffer ends in the middle of a surrogate pair. + UC_Assert ( len == 2 ); + utf16Left -= len; + utf16Pos += len; + utf32Left -= 1; + utf32Pos += 1; + } + + } + +Done: // Set the output lengths. + *utf16Read = utf16Len - utf16Left; + *utf32Written = utf32Len - utf32Left; + +} // UTF16Swp_to_UTF32Nat + +// ================================================================================================= + +static void UTF32Nat_to_UTF16Swp ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf32Read, size_t * utf16Written ) +{ + const UTF32Unit * utf32Pos = utf32In; + UTF16Unit * utf16Pos = utf16Out; + + size_t utf32Left = utf32Len; + size_t utf16Left = utf16Len; + + UC_Assert ( (utf32In != 0) && (utf16Out != 0) && (utf32Read != 0) && (utf16Written != 0) ); + + while ( (utf32Left > 0) && (utf16Left > 0) ) { + + // Do a run of BMP, it copies 1 input unit into 1 output unit. + size_t i, limit = utf32Left; + if ( limit > utf16Left ) limit = utf16Left; + for ( i = 0; i < limit; ++i ) { + UTF32Unit inUnit = *utf32Pos; + if ( inUnit > 0xFFFF ) break; + UTF16OutSwap ( utf16Pos, UTF16Unit(inUnit) ); + ++utf32Pos; + ++utf16Pos; + } + utf32Left -= i; + utf16Left -= i; + + // Do a run of non-BMP, it copies 1 input unit into 2 output units. + while ( (utf32Left > 0) && (utf16Left > 0) ) { + size_t len; + UTF32Unit inUnit = *utf32Pos; + if ( inUnit <= 0xFFFF ) break; + CodePoint_to_UTF16Swp_Surrogate ( inUnit, utf16Pos, utf16Left, &len ); + if ( len == 0 ) goto Done; // Not enough room in the output buffer. + UC_Assert ( len == 2 ); + utf32Left -= 1; + utf32Pos += 1; + utf16Left -= 2; + utf16Pos += 2; + } + + } + +Done: // Set the output lengths. + *utf32Read = utf32Len - utf32Left; + *utf16Written = utf16Len - utf16Left; + +} // UTF32Nat_to_UTF16Swp + +// ================================================================================================= + +static void UTF32Swp_to_UTF16Nat ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf32Read, size_t * utf16Written ) +{ + const UTF32Unit * utf32Pos = utf32In; + UTF16Unit * utf16Pos = utf16Out; + + size_t utf32Left = utf32Len; + size_t utf16Left = utf16Len; + + UC_Assert ( (utf32In != 0) && (utf16Out != 0) && (utf32Read != 0) && (utf16Written != 0) ); + + while ( (utf32Left > 0) && (utf16Left > 0) ) { + + // Do a run of BMP, it copies 1 input unit into 1 output unit. + size_t i, limit = utf32Left; + if ( limit > utf16Left ) limit = utf16Left; + for ( i = 0; i < limit; ++i ) { + UTF32Unit inUnit = UTF32InSwap(utf32Pos); + if ( inUnit > 0xFFFF ) break; + *utf16Pos = UTF16Unit(inUnit); + ++utf32Pos; + ++utf16Pos; + } + utf32Left -= i; + utf16Left -= i; + + // Do a run of non-BMP, it copies 1 input unit into 2 output units. + while ( (utf32Left > 0) && (utf16Left > 0) ) { + size_t len; + UTF32Unit inUnit = UTF32InSwap(utf32Pos); + if ( inUnit <= 0xFFFF ) break; + CodePoint_to_UTF16Nat_Surrogate ( inUnit, utf16Pos, utf16Left, &len ); + if ( len == 0 ) goto Done; // Not enough room in the output buffer. + UC_Assert ( len == 2 ); + utf32Left -= 1; + utf32Pos += 1; + utf16Left -= 2; + utf16Pos += 2; + } + + } + +Done: // Set the output lengths. + *utf32Read = utf32Len - utf32Left; + *utf16Written = utf16Len - utf16Left; + +} // UTF32Swp_to_UTF16Nat + +// ================================================================================================= diff --git a/source/lib/xmp_core/UnicodeConversions.hpp b/source/lib/xmp_core/UnicodeConversions.hpp new file mode 100644 index 0000000..f09437c --- /dev/null +++ b/source/lib/xmp_core/UnicodeConversions.hpp @@ -0,0 +1,115 @@ +#ifndef __UnicodeConversions_h__ +#define __UnicodeConversions_h__ + +// ================================================================================================= +// Copyright 2004 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include + +// ================================================================================================= + +typedef XMP_Uns8 UTF8Unit; +typedef XMP_Uns16 UTF16Unit; +typedef XMP_Uns32 UTF32Unit; + +// ------------------------------------------------------------------------------------------------- + +// ! The UTF16 and UTF32 counts are in storage units, not bytes! CodePoint values are always native. + +// *** MIght be better to return a status than throw an exception for errors? + +typedef void (*CodePoint_to_UTF16_Proc) ( const UTF32Unit cpIn, UTF16Unit * utf16Out, const size_t utf16Len, size_t * utf16Written ); + +typedef void (*CodePoint_from_UTF16_Proc) ( const UTF16Unit * utf16In, const size_t utf16Len, UTF32Unit * cpOut, size_t * utf16Read ); + +typedef void (*UTF8_to_UTF16_Proc) ( const UTF8Unit * utf8In, const size_t utf8Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf8Read, size_t * utf16Written ); + +typedef void (*UTF8_to_UTF32_Proc) ( const UTF8Unit * utf8In, const size_t utf8Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf8Read, size_t * utf32Written ); + +typedef void (*UTF16_to_UTF8_Proc) ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF8Unit * utf8Out, const size_t utf8Len, + size_t * utf16Read, size_t * utf8Written ); + +typedef void (*UTF32_to_UTF8_Proc) ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF8Unit * utf8Out, const size_t utf8Len, + size_t * utf32Read, size_t * utf8Written ); + +typedef void (*UTF16_to_UTF32_Proc) ( const UTF16Unit * utf16In, const size_t utf16Len, + UTF32Unit * utf32Out, const size_t utf32Len, + size_t * utf16Read, size_t * utf32Written ); + +typedef void (*UTF32_to_UTF16_Proc) ( const UTF32Unit * utf32In, const size_t utf32Len, + UTF16Unit * utf16Out, const size_t utf16Len, + size_t * utf32Read, size_t * utf16Written ); + +// ------------------------------------------------------------------------------------------------- + +extern void CodePoint_to_UTF8 ( const UTF32Unit cpIn, UTF8Unit * utf8Out, const size_t utf8Len, size_t * utf8Written ); + +extern void CodePoint_from_UTF8 ( const UTF8Unit * utf8In, const size_t utf8Len, UTF32Unit * cpOut, size_t * utf8Read ); + +extern CodePoint_to_UTF16_Proc CodePoint_to_UTF16BE; +extern CodePoint_to_UTF16_Proc CodePoint_to_UTF16LE; + +extern CodePoint_from_UTF16_Proc CodePoint_from_UTF16BE; +extern CodePoint_from_UTF16_Proc CodePoint_from_UTF16LE; + +extern UTF8_to_UTF16_Proc UTF8_to_UTF16BE; +extern UTF8_to_UTF16_Proc UTF8_to_UTF16LE; + +extern UTF8_to_UTF32_Proc UTF8_to_UTF32BE; +extern UTF8_to_UTF32_Proc UTF8_to_UTF32LE; + +extern UTF16_to_UTF8_Proc UTF16BE_to_UTF8; +extern UTF16_to_UTF8_Proc UTF16LE_to_UTF8; + +extern UTF32_to_UTF8_Proc UTF32BE_to_UTF8; +extern UTF32_to_UTF8_Proc UTF32LE_to_UTF8; + +extern UTF8_to_UTF16_Proc UTF8_to_UTF16Native; +extern UTF8_to_UTF32_Proc UTF8_to_UTF32Native; + +extern UTF16_to_UTF8_Proc UTF16Native_to_UTF8; +extern UTF32_to_UTF8_Proc UTF32Native_to_UTF8; + +extern UTF16_to_UTF32_Proc UTF16BE_to_UTF32BE; +extern UTF16_to_UTF32_Proc UTF16BE_to_UTF32LE; + +extern UTF16_to_UTF32_Proc UTF16LE_to_UTF32BE; +extern UTF16_to_UTF32_Proc UTF16LE_to_UTF32LE; + +extern UTF32_to_UTF16_Proc UTF32BE_to_UTF16BE; +extern UTF32_to_UTF16_Proc UTF32BE_to_UTF16LE; + +extern UTF32_to_UTF16_Proc UTF32LE_to_UTF16BE; +extern UTF32_to_UTF16_Proc UTF32LE_to_UTF16LE; + +extern void SwapUTF16 ( const UTF16Unit * utf16In, UTF16Unit * utf16Out, const size_t utf16Len ); +extern void SwapUTF32 ( const UTF32Unit * utf32In, UTF32Unit * utf32Out, const size_t utf32Len ); + +extern void ToUTF16 ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf16Str, bool bigEndian ); +extern void ToUTF32 ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf32Str, bool bigEndian ); + +extern void FromUTF16 ( const UTF16Unit * utf16In, size_t utf16Len, std::string * utf8Str, bool bigEndian ); +extern void FromUTF32 ( const UTF32Unit * utf32In, size_t utf32Len, std::string * utf8Str, bool bigEndian ); + +extern void ToUTF16Native ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf16Str ); +extern void ToUTF32Native ( const UTF8Unit * utf8In, size_t utf8Len, std::string * utf32Str ); + +extern void FromUTF16Native ( const UTF16Unit * utf16In, size_t utf16Len, std::string * utf8Str ); +extern void FromUTF32Native ( const UTF32Unit * utf32In, size_t utf32Len, std::string * utf8Str ); + +extern void InitializeUnicodeConversions(); + +// ================================================================================================= + +#endif // __UnicodeConversions_h__ diff --git a/source/lib/xmp_core/UnicodeInlines.incl_cpp b/source/lib/xmp_core/UnicodeInlines.incl_cpp new file mode 100644 index 0000000..d96d370 --- /dev/null +++ b/source/lib/xmp_core/UnicodeInlines.incl_cpp @@ -0,0 +1,129 @@ +#ifndef __UnicodeInlines_incl_cpp__ +#define __UnicodeInlines_incl_cpp__ + +// ================================================================================================= +// Copyright 2004 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "UnicodeConversions.hpp" + +// ================================================================================================= +// Inner loop utilities that need to be inlined. +// ================================================================================================= + +static inline XMP_Uns32 GetCodePoint ( const XMP_Uns8 ** utf8Str_io ) +{ + const XMP_Uns8 * u8Ptr = *utf8Str_io; + XMP_Uns32 cp; + size_t u8Len; + CodePoint_from_UTF8 ( u8Ptr, 4, &cp, &u8Len ); // Throws an exception for errors. + *utf8Str_io = u8Ptr + u8Len; + return cp; +} + +// ================================================================================================= + +static inline bool IsStartChar_ASCII ( XMP_Uns32 cp ) +{ + // ASCII starting characters for an XML name. + if ( (('a' <= cp) && (cp <= 'z')) || (('A' <= cp) && (cp <= 'Z')) || (cp == '_') ) return true; + return false; +} + +// ------------------------------------------------------------------------------------------------- + +static inline bool IsStartChar_NonASCII ( XMP_Uns32 cp ) +{ + // Non-ASCII starting characters for an XML name. + + if ( ((0xC0 <= cp) && (cp <= 0xD6)) || ((0xD8 <= cp) && (cp <= 0xF6)) ) return true; + if ( ((0xF8 <= cp) && (cp <= 0x2FF)) || ((0x370 <= cp) && (cp <= 0x37D)) ) return true; + + if ( ((0x37F <= cp) && (cp <= 0x1FFF)) || ((0x200C <= cp) && (cp <= 0x200D)) ) return true; + if ( ((0x2070 <= cp) && (cp <= 0x218F)) || ((0x2C00 <= cp) && (cp <= 0x2FEF)) ) return true; + if ( ((0x3001 <= cp) && (cp <= 0xD7FF)) || ((0xF900 <= cp) && (cp <= 0xFDCF)) ) return true; + if ( ((0xFDF0 <= cp) && (cp <= 0xFFFD)) || ((0x10000 <= cp) && (cp <= 0xEFFFF)) ) return true; + + return false; + +} + +// ------------------------------------------------------------------------------------------------- + +static inline bool IsOtherChar_ASCII ( XMP_Uns32 cp ) +{ + // ASCII following characters for an XML name. + if ( (('0' <= cp) && (cp <= '9')) || (cp == '-') || (cp == '.') ) return true; + return false; +} + +// ------------------------------------------------------------------------------------------------- + +static inline bool IsOtherChar_NonASCII ( XMP_Uns32 cp ) +{ + // Non-ASCII following characters for an XML name. + if ( (cp == 0xB7) || ((0x300 <= cp) && (cp <= 0x36F)) || ((0x203F <= cp) && (cp <= 0x2040)) ) return true; + return false; +} + +// ------------------------------------------------------------------------------------------------- + +static inline void VerifyUTF8 ( XMP_StringPtr str ) +{ + const XMP_Uns8 * utf8Str = (XMP_Uns8*)str; + while ( *utf8Str != 0 ) { + while ( (*utf8Str != 0) && (*utf8Str < 0x80) ) ++utf8Str; + if ( *utf8Str >= 0x80 ) (void) GetCodePoint ( &utf8Str ); // Throws for bad UTF-8. + } +} + +// ------------------------------------------------------------------------------------------------- + +static inline void VerifySimpleXMLName ( XMP_StringPtr _nameStart, XMP_StringPtr _nameEnd ) +{ + + const XMP_Uns8 * nameStart = (const XMP_Uns8 *) _nameStart; + const XMP_Uns8 * nameEnd = (const XMP_Uns8 *) _nameEnd; + const XMP_Uns8 * namePos = nameStart; + XMP_Uns32 cp; + + // The first character is more restricted. + + if ( nameStart >= nameEnd ) XMP_Throw ( "Empty XML name", kXMPErr_BadXPath ); + + cp = *namePos; + if ( cp < 0x80 ) { + ++namePos; + if ( ! IsStartChar_ASCII(cp) ) goto NameError; + } else { + cp = GetCodePoint ( &namePos ); + if ( ! IsStartChar_NonASCII(cp) ) goto NameError; + } + + // Check the rest of the name. + + while ( namePos < nameEnd ) { + cp = *namePos; + if ( cp < 0x80 ) { + ++namePos; + if ( (! IsStartChar_ASCII(cp)) && (! IsOtherChar_ASCII(cp)) ) goto NameError; + } else { + cp = GetCodePoint ( &namePos ); + if ( (! IsStartChar_NonASCII(cp)) && (! IsOtherChar_NonASCII(cp)) ) goto NameError; + } + } + + return; + +NameError: + XMP_Throw ( "Bad XML name", kXMPErr_BadXPath ); + +} // VerifySimpleXMLName + +// ================================================================================================= + +#endif // __UnicodeInlines_incl_cpp__ diff --git a/source/lib/xmp_core/WXMPIterator.cpp b/source/lib/xmp_core/WXMPIterator.cpp new file mode 100644 index 0000000..f96323f --- /dev/null +++ b/source/lib/xmp_core/WXMPIterator.cpp @@ -0,0 +1,170 @@ +// ================================================================================================= +// Copyright 2004 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! This must be the first include! +#include "public/include/XMP_Const.h" + +#include "public/include/client-glue/WXMPIterator.hpp" + +#include "XMPCore_Impl.hpp" +#include "XMPIterator.hpp" + +#if XMP_WinBuild + #pragma warning ( disable : 4101 ) // unreferenced local variable + #pragma warning ( disable : 4189 ) // local variable is initialized but not referenced + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) + #if XMP_DebugBuild + #pragma warning ( disable : 4297 ) // function assumed not to throw an exception but does + #endif +#endif + +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= +// CTor/DTor Wrappers +// ================== + +void +WXMPIterator_PropCTor_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPIterator_PropCTor_1" ) // No lib object yet, use the static entry. + + if ( schemaNS == 0 ) schemaNS = ""; + if ( propName == 0 ) propName = ""; + + const XMPMeta & xmpObj = WtoXMPMeta_Ref ( xmpRef ); + XMP_AutoLock metaLock ( &xmpObj.lock, kXMP_ReadLock ); + + XMPIterator * iter = new XMPIterator ( xmpObj, schemaNS, propName, options ); + ++iter->clientRefs; + XMP_Assert ( iter->clientRefs == 1 ); + wResult->ptrResult = XMPIteratorRef ( iter ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPIterator_TableCTor_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPIterator_TableCTor_1" ) // No lib object yet, use the static entry. + + if ( schemaNS == 0 ) schemaNS = ""; + if ( propName == 0 ) propName = ""; + + XMPIterator * iter = new XMPIterator ( schemaNS, propName, options ); + ++iter->clientRefs; + XMP_Assert ( iter->clientRefs == 1 ); + wResult->ptrResult = XMPIteratorRef ( iter ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPIterator_IncrementRefCount_1 ( XMPIteratorRef xmpObjRef ) +{ + WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro. + XMP_ENTER_ObjWrite ( XMPIterator, "WXMPIterator_IncrementRefCount_1" ) + + ++thiz->clientRefs; + XMP_Assert ( thiz->clientRefs > 1 ); + + XMP_EXIT_NoThrow +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPIterator_DecrementRefCount_1 ( XMPIteratorRef xmpObjRef ) +{ + WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro. + XMP_ENTER_ObjWrite ( XMPIterator, "WXMPIterator_DecrementRefCount_1" ) + + XMP_Assert ( thiz->clientRefs > 0 ); + --thiz->clientRefs; + if ( thiz->clientRefs <= 0 ) { + objLock.Release(); + delete ( thiz ); + } + + XMP_EXIT_NoThrow +} + +// ================================================================================================= +// Class Method Wrappers +// ===================== + +void +WXMPIterator_Next_1 ( XMPIteratorRef xmpObjRef, + void * schemaNS, + void * propPath, + void * propValue, + XMP_OptionBits * propOptions, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPIterator, "WXMPIterator_Next_1" ) + + XMP_StringPtr schemaPtr = 0; + XMP_StringLen schemaLen = 0; + XMP_StringPtr pathPtr = 0; + XMP_StringLen pathLen = 0; + XMP_StringPtr valuePtr = 0; + XMP_StringLen valueLen = 0; + + if ( propOptions == 0 ) propOptions = &voidOptionBits; + + XMP_Assert( thiz->info.xmpObj != NULL ); + XMP_AutoLock metaLock ( &thiz->info.xmpObj->lock, kXMP_ReadLock, (thiz->info.xmpObj != 0) ); + + XMP_Bool found = thiz->Next ( &schemaPtr, &schemaLen, &pathPtr, &pathLen, &valuePtr, &valueLen, propOptions ); + wResult->int32Result = found; + + if ( found ) { + if ( schemaNS != 0 ) (*SetClientString) ( schemaNS, schemaPtr, schemaLen ); + if ( propPath != 0 ) (*SetClientString) ( propPath, pathPtr, pathLen ); + if ( propValue != 0 ) (*SetClientString) ( propValue, valuePtr, valueLen ); + } + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPIterator_Skip_1 ( XMPIteratorRef xmpObjRef, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPIterator, "WXMPIterator_Skip_1" ) + + XMP_Assert( thiz->info.xmpObj != NULL ); + XMP_AutoLock metaLock ( &thiz->info.xmpObj->lock, kXMP_ReadLock, (thiz->info.xmpObj != 0) ); + + thiz->Skip ( options ); + + XMP_EXIT +} + +// ================================================================================================= + +#if __cplusplus +} /* extern "C" */ +#endif diff --git a/source/lib/xmp_core/WXMPMeta.cpp b/source/lib/xmp_core/WXMPMeta.cpp new file mode 100644 index 0000000..956f08f --- /dev/null +++ b/source/lib/xmp_core/WXMPMeta.cpp @@ -0,0 +1,1191 @@ +// ================================================================================================= +// Copyright 2004 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! This must be the first include! +#include "public/include/XMP_Const.h" + +#include "public/include/client-glue/WXMPMeta.hpp" + +#include "XMPCore_Impl.hpp" +#include "XMPMeta.hpp" + +#if XMP_WinBuild + #pragma warning ( disable : 4101 ) // unreferenced local variable + #pragma warning ( disable : 4189 ) // local variable is initialized but not referenced + #pragma warning ( disable : 4702 ) // unreachable code + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) + #if XMP_DebugBuild + #pragma warning ( disable : 4297 ) // function assumed not to throw an exception but does + #endif +#endif + +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= +// Init/Term Wrappers +// ================== + +/* class static */ void +WXMPMeta_GetVersionInfo_1 ( XMP_VersionInfo * info ) +{ + WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro. + XMP_ENTER_NoLock ( "WXMPMeta_GetVersionInfo_1" ) + + XMPMeta::GetVersionInfo ( info ); + + XMP_EXIT_NoThrow +} + +// ------------------------------------------------------------------------------------------------- + +/* class static */ void +WXMPMeta_Initialize_1 ( WXMP_Result * wResult ) +{ + XMP_ENTER_NoLock ( "WXMPMeta_Initialize_1" ) + + wResult->int32Result = XMPMeta::Initialize(); + + XMP_EXIT +} +// ------------------------------------------------------------------------------------------------- + +/* class static */ void +WXMPMeta_Terminate_1() +{ + WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro. + XMP_ENTER_NoLock ( "WXMPMeta_Terminate_1" ) + + XMPMeta::Terminate(); + + XMP_EXIT_NoThrow +} + +// ================================================================================================= +// CTor/DTor Wrappers +// ================== + +void +WXMPMeta_CTor_1 ( WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPMeta_CTor_1" ) // No lib object yet, use the static entry. + + XMPMeta * xmpObj = new XMPMeta(); + ++xmpObj->clientRefs; + XMP_Assert ( xmpObj->clientRefs == 1 ); + wResult->ptrResult = XMPMetaRef ( xmpObj ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_IncrementRefCount_1 ( XMPMetaRef xmpObjRef ) +{ + WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro. + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_IncrementRefCount_1" ) + + ++thiz->clientRefs; + XMP_Assert ( thiz->clientRefs > 0 ); + + XMP_EXIT_NoThrow +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_DecrementRefCount_1 ( XMPMetaRef xmpObjRef ) +{ + WXMP_Result * wResult = &void_wResult; // ! Needed to "fool" the EnterWrapper macro. + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DecrementRefCount_1" ) + + XMP_Assert ( thiz->clientRefs > 0 ); + --thiz->clientRefs; + if ( thiz->clientRefs <= 0 ) { + objLock.Release(); + delete ( thiz ); + } + + XMP_EXIT_NoThrow +} + +// ================================================================================================= +// Class Static Wrappers +// ===================== + +/* class static */ void +WXMPMeta_GetGlobalOptions_1 ( WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPMeta_GetGlobalOptions_1" ) + + XMP_OptionBits options = XMPMeta::GetGlobalOptions(); + wResult->int32Result = options; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +/* class static */ void +WXMPMeta_SetGlobalOptions_1 ( XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPMeta_SetGlobalOptions_1" ) + + XMPMeta::SetGlobalOptions ( options ); + + XMP_EXIT +} +// ------------------------------------------------------------------------------------------------- + +/* class static */ void +WXMPMeta_DumpNamespaces_1 ( XMP_TextOutputProc outProc, + void * refCon, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPMeta_DumpNamespaces_1" ) + + if ( outProc == 0 ) XMP_Throw ( "Null client output routine", kXMPErr_BadParam ); + + XMP_Status status = XMPMeta::DumpNamespaces ( outProc, refCon ); + wResult->int32Result = status; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +/* class static */ void +WXMPMeta_RegisterNamespace_1 ( XMP_StringPtr namespaceURI, + XMP_StringPtr suggestedPrefix, + void * actualPrefix, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPMeta_RegisterNamespace_1" ) + + if ( (namespaceURI == 0) || (*namespaceURI == 0) ) XMP_Throw ( "Empty namespace URI", kXMPErr_BadSchema ); + if ( (suggestedPrefix == 0) || (*suggestedPrefix == 0) ) XMP_Throw ( "Empty suggested prefix", kXMPErr_BadSchema ); + + XMP_StringPtr prefixPtr = 0; + XMP_StringLen prefixSize = 0; + + bool prefixMatch = XMPMeta::RegisterNamespace ( namespaceURI, suggestedPrefix, &prefixPtr, &prefixSize ); + wResult->int32Result = prefixMatch; + + if ( actualPrefix != 0 ) (*SetClientString) ( actualPrefix, prefixPtr, prefixSize ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +/* class static */ void +WXMPMeta_GetNamespacePrefix_1 ( XMP_StringPtr namespaceURI, + void * namespacePrefix, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPMeta_GetNamespacePrefix_1" ) + + if ( (namespaceURI == 0) || (*namespaceURI == 0) ) XMP_Throw ( "Empty namespace URI", kXMPErr_BadSchema ); + + XMP_StringPtr prefixPtr = 0; + XMP_StringLen prefixSize = 0; + + bool found = XMPMeta::GetNamespacePrefix ( namespaceURI, &prefixPtr, &prefixSize ); + wResult->int32Result = found; + + if ( found && (namespacePrefix != 0) ) (*SetClientString) ( namespacePrefix, prefixPtr, prefixSize ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +/* class static */ void +WXMPMeta_GetNamespaceURI_1 ( XMP_StringPtr namespacePrefix, + void * namespaceURI, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPMeta_GetNamespaceURI_1" ) + + if ( (namespacePrefix == 0) || (*namespacePrefix == 0) ) XMP_Throw ( "Empty namespace prefix", kXMPErr_BadSchema ); + + XMP_StringPtr uriPtr = 0; + XMP_StringLen uriSize = 0; + + bool found = XMPMeta::GetNamespaceURI ( namespacePrefix, &uriPtr, &uriSize ); + wResult->int32Result = found; + + if ( found && (namespaceURI != 0) ) (*SetClientString) ( namespaceURI, uriPtr, uriSize ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +/* class static */ void +WXMPMeta_DeleteNamespace_1 ( XMP_StringPtr namespaceURI, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPMeta_DeleteNamespace_1" ) + + if ( (namespaceURI == 0) || (*namespaceURI == 0) ) XMP_Throw ( "Empty namespace URI", kXMPErr_BadSchema ); + + XMPMeta::DeleteNamespace ( namespaceURI ); + + XMP_EXIT +} + +// ================================================================================================= +// Class Method Wrappers +// ===================== + +void +WXMPMeta_GetProperty_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + void * propValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + XMP_StringPtr valuePtr = 0; + XMP_StringLen valueSize = 0; + if ( options == 0 ) options = &voidOptionBits; + + bool found = thiz.GetProperty ( schemaNS, propName, &valuePtr, &valueSize, options ); + wResult->int32Result = found; + + if ( found && (propValue != 0) ) (*SetClientString) ( propValue, valuePtr, valueSize ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_GetArrayItem_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + void * itemValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetArrayItem_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + + XMP_StringPtr valuePtr = 0; + XMP_StringLen valueSize = 0; + if ( options == 0 ) options = &voidOptionBits; + + bool found = thiz.GetArrayItem ( schemaNS, arrayName, itemIndex, &valuePtr, &valueSize, options ); + wResult->int32Result = found; + + if ( found && (itemValue != 0) ) (*SetClientString) ( itemValue, valuePtr, valueSize ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_GetStructField_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + void * fieldValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetStructField_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath ); + if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema ); + if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath ); + + XMP_StringPtr valuePtr = 0; + XMP_StringLen valueSize = 0; + if ( options == 0 ) options = &voidOptionBits; + + bool found = thiz.GetStructField ( schemaNS, structName, fieldNS, fieldName, &valuePtr, &valueSize, options ); + wResult->int32Result = found; + + if ( found && (fieldValue != 0) ) (*SetClientString) ( fieldValue, valuePtr, valueSize ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_GetQualifier_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + void * qualValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetQualifier_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema ); + if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath ); + + XMP_StringPtr valuePtr = 0; + XMP_StringLen valueSize = 0; + if ( options == 0 ) options = &voidOptionBits; + + bool found = thiz.GetQualifier ( schemaNS, propName, qualNS, qualName, &valuePtr, &valueSize, options ); + wResult->int32Result = found; + + if ( found && (qualValue != 0) ) (*SetClientString) ( qualValue, valuePtr, valueSize ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetProperty_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr propValue, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + thiz->SetProperty ( schemaNS, propName, propValue, options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetArrayItem_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_StringPtr itemValue, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetArrayItem_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + + thiz->SetArrayItem ( schemaNS, arrayName, itemIndex, itemValue, options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_AppendArrayItem_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + XMP_StringPtr itemValue, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_AppendArrayItem_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + + thiz->AppendArrayItem ( schemaNS, arrayName, arrayOptions, itemValue, options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetStructField_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetStructField_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath ); + if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema ); + if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath ); + + thiz->SetStructField ( schemaNS, structName, fieldNS, fieldName, fieldValue, options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetQualifier_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_StringPtr qualValue, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetQualifier_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema ); + if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath ); + + thiz->SetQualifier ( schemaNS, propName, qualNS, qualName, qualValue, options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_DeleteProperty_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteProperty_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + thiz->DeleteProperty ( schemaNS, propName ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_DeleteArrayItem_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteArrayItem_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + + thiz->DeleteArrayItem ( schemaNS, arrayName, itemIndex ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_DeleteStructField_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteStructField_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath ); + if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema ); + if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath ); + + thiz->DeleteStructField ( schemaNS, structName, fieldNS, fieldName ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_DeleteQualifier_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteQualifier_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema ); + if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath ); + + thiz->DeleteQualifier ( schemaNS, propName, qualNS, qualName ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_DoesPropertyExist_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DoesPropertyExist_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + bool found = thiz.DoesPropertyExist ( schemaNS, propName ); + wResult->int32Result = found; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_DoesArrayItemExist_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DoesArrayItemExist_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + + bool found = thiz.DoesArrayItemExist ( schemaNS, arrayName, itemIndex ); + wResult->int32Result = found; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_DoesStructFieldExist_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DoesStructFieldExist_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath ); + if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema ); + if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath ); + + bool found = thiz.DoesStructFieldExist ( schemaNS, structName, fieldNS, fieldName ); + wResult->int32Result = found; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_DoesQualifierExist_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DoesQualifierExist_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema ); + if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath ); + + bool found = thiz.DoesQualifierExist ( schemaNS, propName, qualNS, qualName ); + wResult->int32Result = found; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_GetLocalizedText_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + void * actualLang, + void * itemValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetLocalizedText_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + if ( genericLang == 0 ) genericLang = ""; + if ( (specificLang == 0) ||(*specificLang == 0) ) XMP_Throw ( "Empty specific language", kXMPErr_BadParam ); + + XMP_StringPtr langPtr = 0; + XMP_StringLen langSize = 0; + XMP_StringPtr valuePtr = 0; + XMP_StringLen valueSize = 0; + if ( options == 0 ) options = &voidOptionBits; + + bool found = thiz.GetLocalizedText ( schemaNS, arrayName, genericLang, specificLang, + &langPtr, &langSize, &valuePtr, &valueSize, options ); + wResult->int32Result = found; + + if ( found ) { + if ( actualLang != 0 ) (*SetClientString) ( actualLang, langPtr, langSize ); + if ( itemValue != 0 ) (*SetClientString) ( itemValue, valuePtr, valueSize ); + } + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetLocalizedText_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + XMP_StringPtr itemValue, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetLocalizedText_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + if ( genericLang == 0 ) genericLang = ""; + if ( (specificLang == 0) ||(*specificLang == 0) ) XMP_Throw ( "Empty specific language", kXMPErr_BadParam ); + if ( itemValue == 0 ) itemValue = ""; + + thiz->SetLocalizedText ( schemaNS, arrayName, genericLang, specificLang, itemValue, options ); + + XMP_EXIT +} + +void +WXMPMeta_DeleteLocalizedText_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_DeleteLocalizedText_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + if ( genericLang == 0 ) genericLang = ""; + if ( (specificLang == 0) ||(*specificLang == 0) ) XMP_Throw ( "Empty specific language", kXMPErr_BadParam ); + + thiz->DeleteLocalizedText ( schemaNS, arrayName, genericLang, specificLang ); + + XMP_EXIT +} + + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_GetProperty_Bool_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Bool * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Bool_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + if ( propValue == 0 ) propValue = &voidByte; + if ( options == 0 ) options = &voidOptionBits; + + bool value; + bool found = thiz.GetProperty_Bool ( schemaNS, propName, &value, options ); + if ( propValue != 0 ) *propValue = value; + wResult->int32Result = found; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_GetProperty_Int_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Int_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + if ( propValue == 0 ) propValue = &voidInt32; + if ( options == 0 ) options = &voidOptionBits; + + bool found = thiz.GetProperty_Int ( schemaNS, propName, propValue, options ); + wResult->int32Result = found; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_GetProperty_Int64_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Int64_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + if ( propValue == 0 ) propValue = &voidInt64; + if ( options == 0 ) options = &voidOptionBits; + + bool found = thiz.GetProperty_Int64 ( schemaNS, propName, propValue, options ); + wResult->int32Result = found; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_GetProperty_Float_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Float_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + if ( propValue == 0 ) propValue = &voidDouble; + if ( options == 0 ) options = &voidOptionBits; + + bool found = thiz.GetProperty_Float ( schemaNS, propName, propValue, options ); + wResult->int32Result = found; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_GetProperty_Date_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_DateTime * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetProperty_Date_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + if ( propValue == 0 ) propValue = &voidDateTime; + if ( options == 0 ) options = &voidOptionBits; + + bool found = thiz.GetProperty_Date ( schemaNS, propName, propValue, options ); + wResult->int32Result = found; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetProperty_Bool_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Bool propValue, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Bool_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + thiz->SetProperty_Bool ( schemaNS, propName, propValue, options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetProperty_Int_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 propValue, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Int_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + thiz->SetProperty_Int ( schemaNS, propName, propValue, options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetProperty_Int64_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 propValue, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Int64_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + thiz->SetProperty_Int64 ( schemaNS, propName, propValue, options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetProperty_Float_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double propValue, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Float_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + thiz->SetProperty_Float ( schemaNS, propName, propValue, options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetProperty_Date_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const XMP_DateTime & propValue, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetProperty_Date_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + + thiz->SetProperty_Date ( schemaNS, propName, propValue, options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_DumpObject_1 ( XMPMetaRef xmpObjRef, + XMP_TextOutputProc outProc, + void * refCon, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_DumpObject_1" ) + + if ( outProc == 0 ) XMP_Throw ( "Null client output routine", kXMPErr_BadParam ); + + thiz.DumpObject ( outProc, refCon ); + wResult->int32Result = 0; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_Sort_1 ( XMPMetaRef xmpObjRef, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_Sort_1" ) + + thiz->Sort(); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_Erase_1 ( XMPMetaRef xmpObjRef, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_Erase_1" ) + + thiz->Erase(); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_Clone_1 ( XMPMetaRef xmpObjRef, + XMP_OptionBits options, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_Clone_1" ) + + XMPMeta * xClone = new XMPMeta; // ! Don't need an output lock, final ref assignment in client glue. + thiz.Clone ( xClone, options ); + XMP_Assert ( xClone->clientRefs == 0 ); // ! Gets incremented in TXMPMeta::Clone. + wResult->ptrResult = xClone; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_CountArrayItems_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_CountArrayItems_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + + XMP_Index count = thiz.CountArrayItems ( schemaNS, arrayName ); + wResult->int32Result = count; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_GetObjectName_1 ( XMPMetaRef xmpObjRef, + void * objName, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetObjectName_1" ) + + XMP_StringPtr namePtr = 0; + XMP_StringLen nameSize = 0; + + thiz.GetObjectName ( &namePtr, &nameSize ); + if ( objName != 0 ) (*SetClientString) ( objName, namePtr, nameSize ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetObjectName_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr name, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetObjectName_1" ) + + if ( name == 0 ) name = ""; + + thiz->SetObjectName ( name ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_GetObjectOptions_1 ( XMPMetaRef xmpObjRef, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_GetObjectOptions_1" ) + + XMP_OptionBits options = thiz.GetObjectOptions(); + wResult->int32Result = options; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetObjectOptions_1 ( XMPMetaRef xmpObjRef, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetObjectOptions_1" ) + + thiz->SetObjectOptions ( options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_ParseFromBuffer_1 ( XMPMetaRef xmpObjRef, + XMP_StringPtr buffer, + XMP_StringLen bufferSize, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_ParseFromBuffer_1" ) + + thiz->ParseFromBuffer ( buffer, bufferSize, options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SerializeToBuffer_1 ( XMPMetaRef xmpObjRef, + void * pktString, + XMP_OptionBits options, + XMP_StringLen padding, + XMP_StringPtr newline, + XMP_StringPtr indent, + XMP_Index baseIndent, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ +{ + XMP_ENTER_ObjRead ( XMPMeta, "WXMPMeta_SerializeToBuffer_1" ) + + XMP_VarString localStr; + + if ( newline == 0 ) newline = ""; + if ( indent == 0 ) indent = ""; + + thiz.SerializeToBuffer ( &localStr, options, padding, newline, indent, baseIndent ); + if ( pktString != 0 ) (*SetClientString) ( pktString, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetDefaultErrorCallback_1 ( XMPMeta_ErrorCallbackWrapper wrapperProc, + XMPMeta_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPMeta_SetDefaultErrorCallback_1" ) + + XMPMeta::SetDefaultErrorCallback ( wrapperProc, clientProc, context, limit ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_SetErrorCallback_1 ( XMPMetaRef xmpObjRef, + XMPMeta_ErrorCallbackWrapper wrapperProc, + XMPMeta_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_SetErrorCallback_1" ) + + thiz->SetErrorCallback ( wrapperProc, clientProc, context, limit ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPMeta_ResetErrorCallbackLimit_1 ( XMPMetaRef xmpObjRef, + XMP_Uns32 limit, + WXMP_Result * wResult ) +{ + XMP_ENTER_ObjWrite ( XMPMeta, "WXMPMeta_ResetErrorCallbackLimit_1" ) + + thiz->ResetErrorCallbackLimit ( limit ); + + XMP_EXIT +} + +// ================================================================================================= + +#if __cplusplus +} /* extern "C" */ +#endif diff --git a/source/lib/xmp_core/WXMPUtils.cpp b/source/lib/xmp_core/WXMPUtils.cpp new file mode 100644 index 0000000..fc7ca17 --- /dev/null +++ b/source/lib/xmp_core/WXMPUtils.cpp @@ -0,0 +1,634 @@ +// ================================================================================================= +// Copyright 2004 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// *** Should change "type * inParam" to "type & inParam" + +#include "public/include/XMP_Environment.h" // ! This must be the first include! +#include "public/include/XMP_Const.h" + +#include "public/include/client-glue/WXMPUtils.hpp" + +#include "XMPCore_Impl.hpp" +#include "XMPUtils.hpp" + +#if XMP_WinBuild + #pragma warning ( disable : 4101 ) // unreferenced local variable + #pragma warning ( disable : 4189 ) // local variable is initialized but not referenced + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) + #if XMP_DebugBuild + #pragma warning ( disable : 4297 ) // function assumed not to throw an exception but does + #endif +#endif + +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= +// Class Static Wrappers +// ===================== + +void +WXMPUtils_ComposeArrayItemPath_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + void * itemPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ComposeArrayItemPath_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + + XMP_VarString localStr; + + XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &localStr ); + if ( itemPath != 0 ) (*SetClientString) ( itemPath, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ComposeStructFieldPath_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + void * fieldPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ComposeStructFieldPath_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (structName == 0) || (*structName == 0) ) XMP_Throw ( "Empty struct name", kXMPErr_BadXPath ); + if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema ); + if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath ); + + XMP_VarString localStr; + + XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &localStr ); + if ( fieldPath != 0 ) (*SetClientString) ( fieldPath, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ComposeQualifierPath_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + void * qualPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ComposeQualifierPath_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (propName == 0) || (*propName == 0) ) XMP_Throw ( "Empty property name", kXMPErr_BadXPath ); + if ( (qualNS == 0) || (*qualNS == 0) ) XMP_Throw ( "Empty qualifier namespace URI", kXMPErr_BadSchema ); + if ( (qualName == 0) || (*qualName == 0) ) XMP_Throw ( "Empty qualifier name", kXMPErr_BadXPath ); + + XMP_VarString localStr; + + XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &localStr ); + if ( qualPath != 0 ) (*SetClientString) ( qualPath, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ComposeLangSelector_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr langName, + void * selPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ComposeLangSelector_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + if ( (langName == 0) || (*langName == 0) ) XMP_Throw ( "Empty language name", kXMPErr_BadParam ); + + XMP_VarString localStr; + + XMPUtils::ComposeLangSelector ( schemaNS, arrayName, langName, &localStr ); + if ( selPath != 0 ) (*SetClientString) ( selPath, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ComposeFieldSelector_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + void * selPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ComposeFieldSelector_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + if ( (fieldNS == 0) || (*fieldNS == 0) ) XMP_Throw ( "Empty field namespace URI", kXMPErr_BadSchema ); + if ( (fieldName == 0) || (*fieldName == 0) ) XMP_Throw ( "Empty field name", kXMPErr_BadXPath ); + if ( fieldValue == 0 ) fieldValue = ""; + + XMP_VarString localStr; + + XMPUtils::ComposeFieldSelector ( schemaNS, arrayName, fieldNS, fieldName, fieldValue, &localStr ); + if ( selPath != 0 ) (*SetClientString) ( selPath, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ================================================================================================= + +void +WXMPUtils_ConvertFromBool_1 ( XMP_Bool binValue, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ConvertFromBool_1" ) + + XMP_VarString localStr; + + XMPUtils::ConvertFromBool ( binValue, &localStr ); + if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ConvertFromInt_1 ( XMP_Int32 binValue, + XMP_StringPtr format, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ConvertFromInt_1" ) + + if ( format == 0 ) format = ""; + + XMP_VarString localStr; + + XMPUtils::ConvertFromInt ( binValue, format, &localStr ); + if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ConvertFromInt64_1 ( XMP_Int64 binValue, + XMP_StringPtr format, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ConvertFromInt64_1" ) + + if ( format == 0 ) format = ""; + + XMP_VarString localStr; + + XMPUtils::ConvertFromInt64 ( binValue, format, &localStr ); + if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ConvertFromFloat_1 ( double binValue, + XMP_StringPtr format, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ConvertFromFloat_1" ) + + if ( format == 0 ) format = ""; + + XMP_VarString localStr; + + XMPUtils::ConvertFromFloat ( binValue, format, &localStr ); + if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ConvertFromDate_1 ( const XMP_DateTime & binValue, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ConvertFromDate_1" ) + + XMP_VarString localStr; + + XMPUtils::ConvertFromDate( binValue, &localStr ); + if ( strValue != 0 ) (*SetClientString) ( strValue, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ================================================================================================= + +void +WXMPUtils_ConvertToBool_1 ( XMP_StringPtr strValue, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ConvertToBool_1" ) + + if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty string value", kXMPErr_BadParam); + XMP_Bool result = XMPUtils::ConvertToBool ( strValue ); + wResult->int32Result = result; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ConvertToInt_1 ( XMP_StringPtr strValue, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ConvertToInt_1" ) + + if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty string value", kXMPErr_BadParam); + XMP_Int32 result = XMPUtils::ConvertToInt ( strValue ); + wResult->int32Result = result; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ConvertToInt64_1 ( XMP_StringPtr strValue, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ConvertToInt64_1" ) + + if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty string value", kXMPErr_BadParam); + XMP_Int64 result = XMPUtils::ConvertToInt64 ( strValue ); + wResult->int64Result = result; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ConvertToFloat_1 ( XMP_StringPtr strValue, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ConvertToFloat_1") + + if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty string value", kXMPErr_BadParam); + double result = XMPUtils::ConvertToFloat ( strValue ); + wResult->floatResult = result; + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ConvertToDate_1 ( XMP_StringPtr strValue, + XMP_DateTime * binValue, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ConvertToDate_1" ) + + if ( binValue == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam); // ! Pointer is from the client. + XMPUtils::ConvertToDate ( strValue, binValue ); + + XMP_EXIT +} + +// ================================================================================================= + +void +WXMPUtils_CurrentDateTime_1 ( XMP_DateTime * time, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_CurrentDateTime_1" ) + + if ( time == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam); + XMPUtils::CurrentDateTime ( time ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_SetTimeZone_1 ( XMP_DateTime * time, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_SetTimeZone_1" ) + + if ( time == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam); + XMPUtils::SetTimeZone ( time ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ConvertToUTCTime_1 ( XMP_DateTime * time, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ConvertToUTCTime_1" ) + + if ( time == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam); + XMPUtils::ConvertToUTCTime ( time ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ConvertToLocalTime_1 ( XMP_DateTime * time, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ConvertToLocalTime_1" ) + + if ( time == 0 ) XMP_Throw ( "Null output date", kXMPErr_BadParam); + XMPUtils::ConvertToLocalTime ( time ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_CompareDateTime_1 ( const XMP_DateTime & left, + const XMP_DateTime & right, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_CompareDateTime_1" ) + + int result = XMPUtils::CompareDateTime ( left, right ); + wResult->int32Result = result; + + XMP_EXIT +} + +// ================================================================================================= + +void +WXMPUtils_EncodeToBase64_1 ( XMP_StringPtr rawStr, + XMP_StringLen rawLen, + void * encodedStr, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_EncodeToBase64_1" ) + + XMP_VarString localStr; + + XMPUtils::EncodeToBase64 ( rawStr, rawLen, &localStr ); + if ( encodedStr != 0 ) (*SetClientString) ( encodedStr, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_DecodeFromBase64_1 ( XMP_StringPtr encodedStr, + XMP_StringLen encodedLen, + void * rawStr, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_DecodeFromBase64_1" ) + + XMP_VarString localStr; + + XMPUtils::DecodeFromBase64 ( encodedStr, encodedLen, &localStr ); + if ( rawStr != 0 ) (*SetClientString) ( rawStr, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ================================================================================================= + +void +WXMPUtils_PackageForJPEG_1 ( XMPMetaRef wxmpObj, + void * stdStr, + void * extStr, + void * digestStr, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_PackageForJPEG_1" ) + + XMP_VarString localStdStr; + XMP_VarString localExtStr; + XMP_VarString localDigestStr; + + const XMPMeta & xmpObj = WtoXMPMeta_Ref ( wxmpObj ); + XMP_AutoLock metaLock ( &xmpObj.lock, kXMP_ReadLock ); + + XMPUtils::PackageForJPEG ( xmpObj, &localStdStr, &localExtStr, &localDigestStr ); + if ( stdStr != 0 ) (*SetClientString) ( stdStr, localStdStr.c_str(), localStdStr.size() ); + if ( extStr != 0 ) (*SetClientString) ( extStr, localExtStr.c_str(), localExtStr.size() ); + if ( digestStr != 0 ) (*SetClientString) ( digestStr, localDigestStr.c_str(), localDigestStr.size() ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_MergeFromJPEG_1 ( XMPMetaRef wfullXMP, + XMPMetaRef wextendedXMP, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_MergeFromJPEG_1" ) + + if ( wfullXMP == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam ); + if ( wfullXMP == wextendedXMP ) XMP_Throw ( "Full and extended XMP pointers match", kXMPErr_BadParam ); + + XMPMeta * fullXMP = WtoXMPMeta_Ptr ( wfullXMP ); + XMP_AutoLock fullXMPLock ( &fullXMP->lock, kXMP_WriteLock ); + + const XMPMeta & extendedXMP = WtoXMPMeta_Ref ( wextendedXMP ); + XMP_AutoLock extendedXMPLock ( &extendedXMP.lock, kXMP_ReadLock ); + + XMPUtils::MergeFromJPEG ( fullXMP, extendedXMP ); + + XMP_EXIT +} + +// ================================================================================================= + +void +WXMPUtils_CatenateArrayItems_1 ( XMPMetaRef wxmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr separator, + XMP_StringPtr quotes, + XMP_OptionBits options, + void * catedStr, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_CatenateArrayItems_1" ) + + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + + if ( separator == 0 ) separator = "; "; + if ( quotes == 0 ) quotes = "\""; + + XMP_VarString localStr; + + const XMPMeta & xmpObj = WtoXMPMeta_Ref ( wxmpObj ); + XMP_AutoLock metaLock ( &xmpObj.lock, kXMP_ReadLock ); + + XMPUtils::CatenateArrayItems ( xmpObj, schemaNS, arrayName, separator, quotes, options, &localStr ); + if ( catedStr != 0 ) (*SetClientString) ( catedStr, localStr.c_str(), localStr.size() ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_SeparateArrayItems_1 ( XMPMetaRef wxmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + XMP_StringPtr catedStr, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_SeparateArrayItems_1" ) + + if ( wxmpObj == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam ); + if ( (schemaNS == 0) || (*schemaNS == 0) ) XMP_Throw ( "Empty schema namespace URI", kXMPErr_BadSchema ); + if ( (arrayName == 0) || (*arrayName == 0) ) XMP_Throw ( "Empty array name", kXMPErr_BadXPath ); + if ( catedStr == 0 ) catedStr = ""; + + XMPMeta * xmpObj = WtoXMPMeta_Ptr ( wxmpObj ); + XMP_AutoLock metaLock ( &xmpObj->lock, kXMP_WriteLock ); + + XMPUtils::SeparateArrayItems ( xmpObj, schemaNS, arrayName, options, catedStr ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_ApplyTemplate_1 ( XMPMetaRef wWorkingXMP, + XMPMetaRef wTemplateXMP, + XMP_OptionBits actions, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_ApplyTemplate_1" ) + + XMP_Assert ( (wWorkingXMP != 0) && (wTemplateXMP != 0) ); // Client glue enforced. + + XMPMeta * workingXMP = WtoXMPMeta_Ptr ( wWorkingXMP ); + XMP_AutoLock workingLock ( &workingXMP->lock, kXMP_WriteLock ); + + const XMPMeta & templateXMP = WtoXMPMeta_Ref ( wTemplateXMP ); + XMP_AutoLock templateLock ( &templateXMP.lock, kXMP_ReadLock ); + + XMPUtils::ApplyTemplate ( workingXMP, templateXMP, actions ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_RemoveProperties_1 ( XMPMetaRef wxmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_RemoveProperties_1" ) + + if ( wxmpObj == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam ); + if ( schemaNS == 0 ) schemaNS = ""; + if ( propName == 0 ) propName = ""; + + XMPMeta * xmpObj = WtoXMPMeta_Ptr ( wxmpObj ); + XMP_AutoLock metaLock ( &xmpObj->lock, kXMP_WriteLock ); + + XMPUtils::RemoveProperties ( xmpObj, schemaNS, propName, options ); + + XMP_EXIT +} + +// ------------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------------- + +void +WXMPUtils_DuplicateSubtree_1 ( XMPMetaRef wSource, + XMPMetaRef wDest, + XMP_StringPtr sourceNS, + XMP_StringPtr sourceRoot, + XMP_StringPtr destNS, + XMP_StringPtr destRoot, + XMP_OptionBits options, + WXMP_Result * wResult ) +{ + XMP_ENTER_Static ( "WXMPUtils_DuplicateSubtree_1" ) + + if ( wDest == 0 ) XMP_Throw ( "Output XMP pointer is null", kXMPErr_BadParam ); + if ( (sourceNS == 0) || (*sourceNS == 0) ) XMP_Throw ( "Empty source schema URI", kXMPErr_BadSchema ); + if ( (sourceRoot == 0) || (*sourceRoot == 0) ) XMP_Throw ( "Empty source root name", kXMPErr_BadXPath ); + if ( destNS == 0 ) destNS = sourceNS; + if ( destRoot == 0 ) destRoot = sourceRoot; + + const XMPMeta & source = WtoXMPMeta_Ref ( wSource ); + XMP_AutoLock sourceLock ( &source.lock, kXMP_ReadLock, (wSource != wDest) ); + + XMPMeta * dest = WtoXMPMeta_Ptr ( wDest ); + XMP_AutoLock destLock ( &dest->lock, kXMP_WriteLock ); + + XMPUtils::DuplicateSubtree ( source, dest, sourceNS, sourceRoot, destNS, destRoot, options ); + + XMP_EXIT +} + +// ================================================================================================= + +#if __cplusplus +} /* extern "C" */ +#endif diff --git a/source/lib/xmp_core/XMLParserAdapter.hpp b/source/lib/xmp_core/XMLParserAdapter.hpp new file mode 100644 index 0000000..ff9b877 --- /dev/null +++ b/source/lib/xmp_core/XMLParserAdapter.hpp @@ -0,0 +1,155 @@ +#ifndef __XMLParserAdapter_hpp__ +#define __XMLParserAdapter_hpp__ + +// ================================================================================================= +// Copyright 2005 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! Must be the first #include! +#include "public/include/XMP_Const.h" + +#include "XMP_LibUtils.hpp" + +#include +#include + +// ================================================================================================= +// XML_Node details +// +// The XML_Nodes are used only during the XML/RDF parsing process. This presently uses an XML parser +// to create an XML tree, then a recursive descent RDF recognizer to build the corresponding XMP. +// This makes it easier to swap XML parsers and provides a clean separation of XML and RDF issues. +// The overall parsing would be faster and use less memory if the RDF recognition were done on the +// fly using a state machine. But it was much easier to write the recursive descent version. The +// current implementation is pretty fast in absolute terms, so being faster might not be crucial. +// +// Like the XMP tree, the XML tree contains vectors of pointers for down links, and offspring have +// a pointer to their parent. Unlike the XMP tree, this is an exact XML document tree. There are no +// introduced top level namespace nodes or rearrangement of the nodes.. +// +// The exact state of namespaces can vary during the XML parsing, depending on the parser in use. +// By the time the RDF recognition is done though, the namespaces must be normalized. All of the +// used namespaces must be registered, this is done automatically if necessary. All of the "live" +// namespace prefixes will be unique. The ns field of an XML_Node is the namespace URI, the name +// field contains a qualified name (prefix:local). This includes default namespace mapping, the +// URI and prefix will be missing only for elements and attributes in no namespace. + +class XML_Node; + +typedef XML_Node * XML_NodePtr; // Handy for things like: XML_Node * a, b; - b is XML_Node, not XML_Node*! + +enum { kRootNode = 0, kElemNode = 1, kAttrNode = 2, kCDataNode = 3, kPINode = 4 }; + +#define IsWhitespaceChar(ch) ( ((ch) == ' ') || ((ch) == 0x09) || ((ch) == 0x0A) || ((ch) == 0x0D) ) + +typedef std::vector XML_NodeVector; +typedef XML_NodeVector::iterator XML_NodePos; +typedef XML_NodeVector::const_iterator XML_cNodePos; + +#if 0 // Pattern for iterating over the children or attributes: + for ( size_t xxNum = 0, xxLim = _node_->_offspring_.size(); xxNum < xxLim; ++xxNum ) { + const XML_NodePtr _curr_ = _node_->_offspring_[xxNum]; + } +#endif + +class XML_Node { +public: + + // Intended for lightweight internal use. Clients are expected to use the data directly. + + XMP_Uns8 kind; + std::string ns, name, value; + size_t nsPrefixLen; + XML_NodePtr parent; + XML_NodeVector attrs; + XML_NodeVector content; + + bool IsWhitespaceNode() const; + bool IsLeafContentNode() const; // An empty element or one with a single character data child node. + bool IsEmptyLeafNode() const; + + XMP_StringPtr GetAttrValue ( XMP_StringPtr attrName ) const; + void SetAttrValue ( XMP_StringPtr attrName, XMP_StringPtr attrValue ); + + XMP_StringPtr GetLeafContentValue() const; + std::string* GetLeafContentPtr() const; + void SetLeafContentValue ( XMP_StringPtr value ); + + size_t CountNamedElements ( XMP_StringPtr nsURI, XMP_StringPtr localName ) const; // Number of child elements with this name. + XML_NodePtr GetNamedElement ( XMP_StringPtr nsURI, XMP_StringPtr localName, size_t which = 0 ); + + void Dump ( std::string * buffer ); + void Serialize ( std::string * buffer ); + + void RemoveAttrs(); + void RemoveContent(); + void ClearNode(); + + XML_Node ( XML_NodePtr _parent, XMP_StringPtr _name, XMP_Uns8 _kind ) + : kind(_kind), name(_name), parent(_parent), nsPrefixLen(0) {}; + + XML_Node ( XML_NodePtr _parent, const std::string & _name, XMP_Uns8 _kind ) + : kind(_kind), name(_name), parent(_parent), nsPrefixLen(0) {}; + + virtual ~XML_Node() { RemoveAttrs(); RemoveContent(); }; + +private: + + XML_Node() : kind(0), parent(0) {}; // ! Hidden to make sure parent pointer is always set. + +}; + +// ================================================================================================= +// Abstract base class for XML parser adapters used by the XMP toolkit. + +enum { kXMLPendingInputMax = 16 }; + +class XMLParserAdapter { +public: + + XMLParserAdapter() : tree(0,"",kRootNode), rootNode(0), rootCount(0), + charEncoding(XMP_OptionBits(-1)), pendingCount(0), + errorCallback(0) + { + #if XMP_DebugBuild + parseLog = 0; + #endif + }; + + virtual ~XMLParserAdapter() {}; + + virtual void ParseBuffer ( const void * buffer, size_t length, bool last ) = 0; + + virtual void SetErrorCallback ( GenericErrorCallback * ec ) + { this->errorCallback = ec; }; + + virtual void NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error ) + { + if (this->errorCallback) + this->errorCallback->NotifyClient( severity, error ); + } + + XML_Node tree; + XML_NodeVector parseStack; + XML_NodePtr rootNode; + size_t rootCount; + + XMP_OptionBits charEncoding; + size_t pendingCount; + unsigned char pendingInput[kXMLPendingInputMax]; // Buffered input for character encoding checks. + + GenericErrorCallback * errorCallback; // Set if the relevant XMPCore or XMPFiles object has one. + + #if XMP_DebugBuild + FILE * parseLog; + #endif + +}; + +// ================================================================================================= + +#endif // __XMLParserAdapter_hpp__ diff --git a/source/lib/xmp_core/XML_Node.cpp b/source/lib/xmp_core/XML_Node.cpp new file mode 100644 index 0000000..c7ac1ce --- /dev/null +++ b/source/lib/xmp_core/XML_Node.cpp @@ -0,0 +1,473 @@ +// ================================================================================================= +// Copyright 2007 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! Must be the first #include! +#include "XMLParserAdapter.hpp" + +#include +#include +#include + +// ! Can't include XMP..._Impl.hpp - used by both Core and Files. +#define XMP_LitNMatch(s,l,n) (std::strncmp((s),(l),(n)) == 0) + +#if XMP_WinBuild + #define snprintf _snprintf + #pragma warning ( disable : 4996 ) // snprintf is safe +#endif + +// ================================================================================================= + +#if 0 // Pattern for iterating over the children or attributes: + for ( size_t xxNum = 0, xxLim = _node_->_offspring_->size(); xxNum < xxLim; ++xxNum ) { + const XML_NodePtr _curr_ = _node_->_offspring_[xxNum]; + } +#endif + +// ================================================================================================= +// XML_Node::IsWhitespaceNode +//=========================== + +bool XML_Node::IsWhitespaceNode() const +{ + if ( this->kind != kCDataNode ) return false; + + for ( size_t i = 0; i < this->value.size(); ++i ) { + unsigned char ch = this->value[i]; + if ( IsWhitespaceChar ( ch ) ) continue; + // *** Add checks for other whitespace characters. + return false; // All the checks failed, this isn't whitespace. + } + + return true; + +} // XML_Node::IsWhitespaceNode + +// ================================================================================================= +// XML_Node::IsLeafContentNode +//============================ + +bool XML_Node::IsLeafContentNode() const +{ + if ( this->kind != kElemNode ) return false; + if ( this->content.size() == 0 ) return true; + if ( this->content.size() > 1 ) return false; + if ( this->content[0]->kind != kCDataNode ) return false; + + return true; + +} // XML_Node::IsLeafContentNode + +// ================================================================================================= +// XML_Node::IsEmptyLeafNode +//========================== + +bool XML_Node::IsEmptyLeafNode() const +{ + + if ( (this->kind != kElemNode) || (this->content.size() != 0) ) return false; + return true; + +} // XML_Node::IsEmptyLeafNode + +// ================================================================================================= +// XML_Node::GetAttrValue +//======================= + +XMP_StringPtr XML_Node::GetAttrValue ( XMP_StringPtr attrName ) const +{ + + for ( size_t i = 0, aLim = this->attrs.size(); i < aLim; ++i ) { + XML_Node * attrPtr = this->attrs[i]; + if ( ! attrPtr->ns.empty() ) continue; // This form of GetAttrValue is for attrs in no namespace. + if ( attrPtr->name == attrName ) return attrPtr->value.c_str(); + } + + return 0; // Not found. + +} // XML_Node::GetAttrValue + +// ================================================================================================= +// XML_Node::SetAttrValue +//======================= + +void XML_Node::SetAttrValue ( XMP_StringPtr attrName, XMP_StringPtr attrValue ) +{ + + for ( size_t i = 0, aLim = this->attrs.size(); i < aLim; ++i ) { + XML_Node * attrPtr = this->attrs[i]; + if ( ! attrPtr->ns.empty() ) continue; // This form of SetAttrValue is for attrs in no namespace. + if ( attrPtr->name == attrName ) { + attrPtr->value = attrValue; + return; + } + } + +} // XML_Node::SetAttrValue + +// ================================================================================================= +// XML_Node::GetLeafContentValue +//============================== + +XMP_StringPtr XML_Node::GetLeafContentValue() const +{ + if ( (! this->IsLeafContentNode()) || this->content.empty() ) return ""; + + return this->content[0]->value.c_str(); + +} // XML_Node::GetLeafContentValue + +// ================================================================================================= +// XML_Node::GetLeafContentValue +//============================== + +std::string* XML_Node::GetLeafContentPtr() const +{ + if ( (! this->IsLeafContentNode()) || this->content.empty() ) return 0; + + return &this->content[0]->value; + +} // XML_Node::GetLeafContentValue + +// ================================================================================================= +// XML_Node::SetLeafContentValue +//============================== + +void XML_Node::SetLeafContentValue ( XMP_StringPtr newValue ) +{ + XML_Node * valueNode; + + if ( ! this->content.empty() ) { + valueNode = this->content[0]; + } else { + valueNode = new XML_Node ( this, "", kCDataNode ); + this->content.push_back ( valueNode ); + } + + valueNode->value = newValue; + +} // XML_Node::SetLeafContentValue + +// ================================================================================================= +// XML_Node::CountNamedElements +//============================= + +size_t XML_Node::CountNamedElements ( XMP_StringPtr nsURI, XMP_StringPtr localName ) const +{ + size_t count = 0; + + for ( size_t i = 0, vLim = this->content.size(); i < vLim; ++i ) { + const XML_Node & child = *this->content[i]; + if ( child.ns != nsURI ) continue; + if ( strcmp ( localName, child.name.c_str()+child.nsPrefixLen ) != 0 ) continue; + ++count; + } + + return count; + +} // XML_Node::CountNamedElements + +// ================================================================================================= +// XML_Node::GetNamedElement +//========================== + +XML_NodePtr XML_Node::GetNamedElement ( XMP_StringPtr nsURI, XMP_StringPtr localName, size_t which /* = 0 */ ) +{ + + for ( size_t i = 0, vLim = this->content.size(); i < vLim; ++i ) { + XML_Node * childPtr = this->content[i]; + if ( childPtr->ns != nsURI ) continue; + if ( strcmp ( localName, childPtr->name.c_str()+childPtr->nsPrefixLen ) != 0 ) continue; + if ( which == 0 ) return childPtr; + --which; + } + + return 0; /// Not found. + +} // XML_Node::GetNamedElement + +// ================================================================================================= +// DumpNodeList +// ============ + +static const char * kNodeKinds[] = { "root", "elem", "attr", "cdata", "pi" }; + +static void DumpNodeList ( std::string * buffer, const XML_NodeVector & list, int indent ) +{ + + for ( size_t i = 0, limit = list.size(); i < limit; ++i ) { + + const XML_Node * node = list[i]; + + for ( int t = indent; t > 0; --t ) *buffer += " "; + if ( node->IsWhitespaceNode() ) { + *buffer += "-- whitespace --\n"; + continue; + } + + *buffer += node->name; + *buffer += " - "; + *buffer += kNodeKinds[node->kind]; + if ( ! node->value.empty() ) { + *buffer += ", value=\""; + *buffer += node->value; + *buffer += "\""; + } + if ( ! node->ns.empty() ) { + *buffer += ", ns=\""; + *buffer += node->ns; + *buffer += "\""; + } + if ( node->nsPrefixLen != 0 ) { + *buffer += ", prefixLen="; + char numBuf [20]; + snprintf ( numBuf, sizeof(numBuf), "%d", (int)node->nsPrefixLen ); + *buffer += numBuf; + } + *buffer += "\n"; + + if ( ! node->attrs.empty() ) { + for ( int t = indent+1; t > 0; --t ) *buffer += " "; + *buffer += "attrs:\n"; + DumpNodeList ( buffer, node->attrs, indent+2 ); + } + + if ( ! node->content.empty() ) { + DumpNodeList ( buffer, node->content, indent+1 ); + } + + } + +} // DumpNodeList + +// ================================================================================================= +// XML_Node::Dump +//=============== + +void XML_Node::Dump ( std::string * buffer ) +{ + + *buffer = "Dump of XML_Node tree\n"; + + *buffer += "Root info: name=\""; + *buffer += this->name; + *buffer += "\", value=\""; + *buffer += this->value; + *buffer += "\", ns=\""; + *buffer += this->ns; + *buffer += "\", kind="; + *buffer += kNodeKinds[this->kind]; + *buffer += "\n"; + + if ( ! this->attrs.empty() ) { + *buffer += " attrs:\n"; + DumpNodeList ( buffer, this->attrs, 2 ); + } + *buffer += "\n"; + + DumpNodeList ( buffer, this->content, 0 ); + +} // XML_Node::Dump + +// ================================================================================================= +// SerializeOneNode +// ================ + +static void SerializeOneNode ( std::string * buffer, const XML_Node & node ) +{ + size_t i, limit; + XMP_StringPtr namePtr = node.name.c_str(); + if ( XMP_LitNMatch ( namePtr, "_dflt_:", 7 ) ) namePtr += 7; // Hack for default namespaces. + + switch ( node.kind ) { + + case kElemNode: + *buffer += '<'; + *buffer += namePtr; + for ( i = 0, limit = node.attrs.size(); i < limit; ++i ) { + SerializeOneNode ( buffer, *node.attrs[i] ); + } + if ( node.content.empty() ) { + *buffer += "/>"; + } else { + *buffer += '>'; + for ( i = 0, limit = node.content.size(); i < limit; ++i ) { + SerializeOneNode ( buffer, *node.content[i] ); + } + *buffer += "'; + } + break; + + case kAttrNode: + *buffer += ' '; + *buffer += namePtr; + *buffer += "=\""; + *buffer += node.value; + *buffer += '"'; + break; + + case kCDataNode: + *buffer += node.value; + break; + + case kPINode: + *buffer += node.value; // *** Note that we're dropping PIs during the Expat parse. + break; + + } + +} // SerializeOneNode + +// ================================================================================================= +// CollectNamespaceDecls +// ===================== + +typedef std::map < std::string, std::string > NamespaceMap; + +static void CollectNamespaceDecls ( NamespaceMap * nsMap, const XML_Node & node ) +{ + size_t i, limit; + + if ( ! node.ns.empty() ) { + size_t nameMid = 0; + while ( node.name[nameMid] != ':' ) ++nameMid; + std::string prefix = node.name.substr ( 0, nameMid ); + (*nsMap)[prefix] = node.ns; + } + + if ( node.kind == kElemNode ) { + + for ( i = 0, limit = node.attrs.size(); i < limit; ++i ) { + CollectNamespaceDecls ( nsMap, *node.attrs[i] ); + } + + for ( i = 0, limit = node.content.size(); i < limit; ++i ) { + const XML_Node & content = *node.content[i]; + if ( content.kind == kElemNode ) CollectNamespaceDecls ( nsMap, content ); + } + + } + +} // CollectNamespaceDecls + +// ================================================================================================= +// XML_Node::Serialize +//==================== + +void XML_Node::Serialize ( std::string * buffer ) +{ + buffer->erase(); + + if ( this->kind != kRootNode ) { + + SerializeOneNode ( buffer, *this ); + + } else { + + // Do the outermost level here, in order to add the XML version and namespace declarations. + + *buffer += "\n"; + + for ( size_t outer = 0, oLimit = this->content.size(); outer < oLimit; ++outer ) { + + const XML_Node & node = *this->content[outer]; + + if ( node.kind != kElemNode ) { + + SerializeOneNode ( buffer, node ); + + } else { + + XMP_StringPtr namePtr = node.name.c_str(); + if ( XMP_LitNMatch ( namePtr, "_dflt_:", 7 ) ) namePtr += 7; // Hack for default namespaces. + + *buffer += '<'; + *buffer += namePtr; + + NamespaceMap nsMap; + CollectNamespaceDecls ( &nsMap, node ); + NamespaceMap::iterator nsDecl = nsMap.begin(); + NamespaceMap::iterator nsEnd = nsMap.end(); + for ( ; nsDecl != nsEnd; ++nsDecl ) { + const std::string & prefix = nsDecl->first; + *buffer += " xmlns"; + if ( prefix != "_dflt_" ) { *buffer += ':'; *buffer += prefix; } + *buffer += "=\""; + *buffer += nsDecl->second; + *buffer += '"'; + } + + for ( size_t attr = 0, aLimit = node.attrs.size(); attr < aLimit; ++attr ) { + SerializeOneNode ( buffer, *node.attrs[attr] ); + } + + if ( node.content.empty() ) { + *buffer += "/>"; + } else { + *buffer += '>'; + for ( size_t child = 0, cLimit = node.content.size(); child < cLimit; ++child ) { + SerializeOneNode ( buffer, *node.content[child] ); + } + *buffer += "'; + } + + } + + } + + } + + +} // XML_Node::Serialize + +// ================================================================================================= +// XML_Node::RemoveAttrs +//====================== + +void XML_Node::RemoveAttrs() +{ + + for ( size_t i = 0, vLim = this->attrs.size(); i < vLim; ++i ) delete this->attrs[i]; + this->attrs.clear(); + +} // XML_Node::RemoveAttrs + +// ================================================================================================= +// XML_Node::RemoveContent +//======================== + +void XML_Node::RemoveContent() +{ + + for ( size_t i = 0, vLim = this->content.size(); i < vLim; ++i ) delete this->content[i]; + this->content.clear(); + +} // XML_Node::RemoveContent + +// ================================================================================================= +// XML_Node::ClearNode +//==================== + +void XML_Node::ClearNode() +{ + + this->kind = 0; + this->ns.erase(); + this->name.erase(); + this->value.erase(); + + this->RemoveAttrs(); + this->RemoveContent(); + +} // XML_Node::ClearNode + +// ================================================================================================= diff --git a/source/lib/xmp_core/XMPCore_Impl.cpp b/source/lib/xmp_core/XMPCore_Impl.cpp new file mode 100644 index 0000000..f98b717 --- /dev/null +++ b/source/lib/xmp_core/XMPCore_Impl.cpp @@ -0,0 +1,1390 @@ +// ================================================================================================= +// Copyright 2004 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include + +#include "public/include/XMP_Environment.h" // ! This must be the first include! +#include "public/include/XMP_Version.h" +#include "XMPCore_Impl.hpp" +#include "XMPMeta.hpp" // *** For use of GetNamespacePrefix in FindSchemaNode. + +#include "UnicodeInlines.incl_cpp" + +using namespace std; + +#if XMP_WinBuild + #pragma warning ( disable : 4290 ) // C++ exception specification ignored except ... not __declspec(nothrow) + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) +#endif + +// ================================================================================================= +// Static Variables +// ================ + +XMP_Int32 sXMP_InitCount = 0; + +XMP_NamespaceTable * sRegisteredNamespaces = 0; + +XMP_AliasMap * sRegisteredAliasMap = 0; + +void * voidVoidPtr = 0; // Used to backfill null output parameters. +XMP_StringPtr voidStringPtr = 0; +XMP_StringLen voidStringLen = 0; +XMP_OptionBits voidOptionBits = 0; +XMP_Uns8 voidByte = 0; +bool voidBool = 0; +XMP_Int32 voidInt32 = 0; +XMP_Int64 voidInt64 = 0; +double voidDouble = 0.0; +XMP_DateTime voidDateTime; +WXMP_Result void_wResult; + +// ================================================================================================= +// Local Utilities +// =============== + +// ------------------------------------------------------------------------------------------------- +// VerifyXPathRoot +// --------------- +// +// Set up the first 2 components of the expanded XPath. Normalizes the various cases of using the +// full schema URI and/or a qualified root property name. Returns true for normal processing. If +// allowUnknownSchemaNS is true and the schema namespace is not registered, false is returned. If +// allowUnknownSchemaNS is false and the schema namespace is not registered, an exception is thrown. + +// *** Should someday check the full syntax. + +static void +VerifyXPathRoot ( XMP_StringPtr schemaURI, + XMP_StringPtr propName, + XMP_ExpandedXPath * expandedXPath ) +{ + // Do some basic checks on the URI and name. Try to lookup the URI. See if the name is qualified. + + XMP_Assert ( (schemaURI != 0) && (propName != 0) && (*propName != 0) ); + XMP_Assert ( (expandedXPath != 0) && (expandedXPath->empty()) ); + + if ( *schemaURI == 0 ) XMP_Throw ( "Schema namespace URI is required", kXMPErr_BadSchema ); + + if ( (*propName == '?') || (*propName == '@') ) { + XMP_Throw ( "Top level name must not be a qualifier", kXMPErr_BadXPath ); + } + for ( XMP_StringPtr ch = propName; *ch != 0; ++ch ) { + if ( (*ch == '/') || (*ch == '[') ) { + XMP_Throw ( "Top level name must be simple", kXMPErr_BadXPath ); + } + } + + XMP_StringPtr schemaPrefix; + bool nsFound = sRegisteredNamespaces->GetPrefix ( schemaURI, &schemaPrefix, 0 ); + if ( ! nsFound ) XMP_Throw ( "Unregistered schema namespace URI", kXMPErr_BadSchema ); + + XMP_StringPtr colonPos = propName; + while ( (*colonPos != 0) && (*colonPos != ':') ) ++colonPos; + VerifySimpleXMLName ( propName, colonPos ); // Verify the part before any colon. + + // Verify the various URI and prefix combinations. Initialize the expanded XPath. + + if ( *colonPos == 0 ) { + + // The propName is unqualified, use the schemaURI and associated prefix. + + expandedXPath->push_back ( XPathStepInfo ( schemaURI, kXMP_SchemaNode ) ); + expandedXPath->push_back ( XPathStepInfo ( schemaPrefix, 0 ) ); + (*expandedXPath)[kRootPropStep].step += propName; + + } else { + + // The propName is qualified. Make sure the prefix is legit. Use the associated URI and qualified name. + + size_t prefixLen = colonPos - propName + 1; // ! Include the colon. + VerifySimpleXMLName ( colonPos+1, colonPos+strlen(colonPos) ); + + XMP_VarString prefix ( propName, prefixLen ); + if ( prefix != schemaPrefix ) XMP_Throw ( "Schema namespace URI and prefix mismatch", kXMPErr_BadSchema ); + + expandedXPath->push_back ( XPathStepInfo ( schemaURI, kXMP_SchemaNode ) ); + expandedXPath->push_back ( XPathStepInfo ( propName, 0 ) ); + + } + +} // VerifyXPathRoot + +// ------------------------------------------------------------------------------------------------- +// VerifyQualName +// -------------- + +static void +VerifyQualName ( XMP_StringPtr qualName, XMP_StringPtr nameEnd ) +{ + if ( qualName >= nameEnd ) XMP_Throw ( "Empty qualified name", kXMPErr_BadXPath ); + + XMP_StringPtr colonPos = qualName; + while ( (colonPos < nameEnd) && (*colonPos != ':') ) ++colonPos; + if ( (colonPos == qualName) || (colonPos >= nameEnd) ) XMP_Throw ( "Ill-formed qualified name", kXMPErr_BadXPath ); + + VerifySimpleXMLName ( qualName, colonPos ); + VerifySimpleXMLName ( colonPos+1, nameEnd ); + + size_t prefixLen = colonPos - qualName + 1; // ! Include the colon. + XMP_VarString prefix ( qualName, prefixLen ); + bool nsFound = sRegisteredNamespaces->GetURI ( prefix.c_str(), 0, 0 ); + if ( ! nsFound ) XMP_Throw ( "Unknown namespace prefix for qualified name", kXMPErr_BadXPath ); + +} // VerifyQualName + +// ------------------------------------------------------------------------------------------------- +// FindIndexedItem +// --------------- +// +// [index] An element of an array. +// +// Support the implicit creation of a new last item. + +static XMP_Index +FindIndexedItem ( XMP_Node * arrayNode, const XMP_VarString & indexStep, bool createNodes ) +{ + XMP_Index index = 0; + size_t chLim = indexStep.size() - 1; + + XMP_Assert ( (chLim >= 2) && (indexStep[0] == '[') && (indexStep[chLim] == ']') ); + + for ( size_t chNum = 1; chNum != chLim; ++chNum ) { + XMP_Assert ( ('0' <= indexStep[chNum]) && (indexStep[chNum] <= '9') ); + index = (index * 10) + (indexStep[chNum] - '0'); + if ( index < 0 ) { + XMP_Throw ( "Array index overflow", kXMPErr_BadXPath ); // ! Overflow, not truly negative. + } + } + + --index; // Change to a C-style, zero based index. + if ( index < 0 ) XMP_Throw ( "Array index must be larger than zero", kXMPErr_BadXPath ); + + if ( (index == (XMP_Index)arrayNode->children.size()) && createNodes ) { // Append a new last+1 node. + XMP_Node * newItem = new XMP_Node ( arrayNode, kXMP_ArrayItemName, kXMP_NewImplicitNode ); + arrayNode->children.push_back ( newItem ); + } + + // ! Don't throw here for a too large index. SetProperty will throw, GetProperty will not. + if ( index >= (XMP_Index)arrayNode->children.size() ) index = -1; + return index; + +} // FindIndexedItem + +// ------------------------------------------------------------------------------------------------- +// SplitNameAndValue +// ----------------- +// +// Split the name and value parts for field and qualifier selectors: +// +// [qualName="value"] An element in an array of structs, chosen by a field value. +// [?qualName="value"] An element in an array, chosen by a qualifier value. +// +// The value portion is a string quoted by ''' or '"'. The value may contain any character including +// a doubled quoting character. The value may be empty. + +static void +SplitNameAndValue ( const XMP_VarString & selStep, XMP_VarString * nameStr, XMP_VarString * valueStr ) +{ + XMP_StringPtr partBegin = selStep.c_str(); + XMP_StringPtr partEnd; + + const XMP_StringPtr valueEnd = partBegin + (selStep.size() - 2); + const char quote = *valueEnd; + + XMP_Assert ( (*partBegin == '[') && (*(valueEnd+1) == ']') ); + XMP_Assert ( (selStep.size() >= 6) && ((quote == '"') || (quote == '\'')) ); + + // Extract the name part. + + ++partBegin; // Skip the opening '['. + if ( *partBegin == '?' ) ++partBegin; + for ( partEnd = partBegin+1; *partEnd != '='; ++partEnd ) {}; + + nameStr->assign ( partBegin, (partEnd - partBegin) ); + + // Extract the value part, reducing doubled quotes. + + XMP_Assert ( *(partEnd+1) == quote ); + + partBegin = partEnd + 2; + valueStr->erase(); + valueStr->reserve ( valueEnd - partBegin ); // Maximum length, don't optimize doubled quotes. + + for ( partEnd = partBegin; partEnd < valueEnd; ++partEnd ) { + if ( (*partEnd == quote) && (*(partEnd+1) == quote) ) { + ++partEnd; + valueStr->append ( partBegin, (partEnd - partBegin) ); + partBegin = partEnd+1; // ! Loop will increment partEnd again. + } + } + + valueStr->append ( partBegin, (partEnd - partBegin) ); // ! The loop does not add the last part. + +} // SplitNameAndValue + +// ------------------------------------------------------------------------------------------------- +// LookupQualSelector +// ------------------ +// +// [?qualName="value"] An element in an array, chosen by a qualifier value. +// +// Note that we don't create implicit nodes for qualifier selectors, so no CreateNodes parameter. + +static XMP_Index +LookupQualSelector ( XMP_Node * arrayNode, const XMP_VarString & qualName, XMP_VarString & qualValue ) +{ + XMP_Index index; + + if ( qualName == "xml:lang" ) { + + // *** Should check that the value is legit RFC 1766/3066. + NormalizeLangValue ( &qualValue ); + index = LookupLangItem ( arrayNode, qualValue ) ; + + } else { + + XMP_Index itemLim; + for ( index = 0, itemLim = arrayNode->children.size(); index != itemLim; ++index ) { + + const XMP_Node * currItem = arrayNode->children[index]; + XMP_Assert ( currItem->parent == arrayNode ); + + size_t q, qualLim; + for ( q = 0, qualLim = currItem->qualifiers.size(); q != qualLim; ++q ) { + const XMP_Node * currQual = currItem->qualifiers[q]; + XMP_Assert ( currQual->parent == currItem ); + if ( currQual->name != qualName ) continue; + if ( currQual->value == qualValue ) break; // Exit qual loop. + } + if ( q != qualLim ) break; // Exit child loop, found an item with a matching qualifier. + + } + if ( index == itemLim ) index = -1; + + } + + return index; + +} // LookupQualSelector + +// ------------------------------------------------------------------------------------------------- +// FollowXPathStep +// --------------- +// +// After processing by ExpandXPath, a step can be of these forms: +// qualName A top level property or struct field. +// [index] An element of an array. +// [last()] The last element of an array. +// [qualName="value"] An element in an array of structs, chosen by a field value. +// [?qualName="value"] An element in an array, chosen by a qualifier value. +// ?qualName A general qualifier. +// +// Find the appropriate child node, resolving aliases, and optionally creating nodes. + +static XMP_Node * +FollowXPathStep ( XMP_Node * parentNode, + const XMP_ExpandedXPath & fullPath, + size_t stepNum, + bool createNodes, + XMP_NodePtrPos * ptrPos, + bool aliasedArrayItem = false ) +{ + XMP_Node * nextNode = 0; + const XPathStepInfo & nextStep = fullPath[stepNum]; + XMP_Index index = 0; + XMP_OptionBits stepKind = nextStep.options & kXMP_StepKindMask; + + XMP_Assert ( (kXMP_StructFieldStep <= stepKind) && (stepKind <= kXMP_FieldSelectorStep) ); + + if ( stepKind == kXMP_StructFieldStep ) { + + nextNode = FindChildNode ( parentNode, nextStep.step.c_str(), createNodes, ptrPos ); + + } else if ( stepKind == kXMP_QualifierStep ) { + + XMP_StringPtr qualStep = nextStep.step.c_str(); + XMP_Assert ( *qualStep == '?' ); + ++qualStep; + nextNode = FindQualifierNode ( parentNode, qualStep, createNodes, ptrPos ); + + } else { + + // This is an array indexing step. First get the index, then get the node. + + if ( ! (parentNode->options & kXMP_PropValueIsArray) ) { + XMP_Throw ( "Indexing applied to non-array", kXMPErr_BadXPath ); + } + + if ( stepKind == kXMP_ArrayIndexStep ) { + index = FindIndexedItem ( parentNode, nextStep.step, createNodes ); + } else if ( stepKind == kXMP_ArrayLastStep ) { + index = parentNode->children.size() - 1; + } else if ( stepKind == kXMP_FieldSelectorStep ) { + XMP_VarString fieldName, fieldValue; + SplitNameAndValue ( nextStep.step, &fieldName, &fieldValue ); + index = LookupFieldSelector ( parentNode, fieldName.c_str(), fieldValue.c_str() ); + } else if ( stepKind == kXMP_QualSelectorStep ) { + XMP_VarString qualName, qualValue; + SplitNameAndValue ( nextStep.step, &qualName, &qualValue ); + index = LookupQualSelector ( parentNode, qualName, qualValue ); + } else { + XMP_Throw ( "Unknown array indexing step in FollowXPathStep", kXMPErr_InternalFailure ); + } + + if ( (0 <= index) && (index <= (XMP_Index)parentNode->children.size()) ) nextNode = parentNode->children[index]; + + if ( (index == -1) && createNodes && aliasedArrayItem && (stepKind == kXMP_QualSelectorStep) ) { + + // An ugly special case without an obvious better place to be. We have an alias to the + // x-default item of an alt-text array. A simple reference via SetProperty must create + // the x-default item if it does not yet exist. + + XMP_Assert ( parentNode->options & kXMP_PropArrayIsAltText ); + XMP_Assert ( (stepNum == 2) && (nextStep.step == "[?xml:lang=\"x-default\"]") ); + + nextNode = new XMP_Node ( parentNode, kXMP_ArrayItemName, + (kXMP_PropHasQualifiers | kXMP_PropHasLang | kXMP_NewImplicitNode) ); + + XMP_Node * langQual = new XMP_Node ( nextNode, "xml:lang", "x-default", kXMP_PropIsQualifier ); + nextNode->qualifiers.push_back ( langQual ); + + if ( parentNode->children.empty() ) { + parentNode->children.push_back ( nextNode ); + } else { + parentNode->children.insert ( parentNode->children.begin(), nextNode ); + } + + index = 0; // ! C-style index! The x-default item is always first. + + } + + if ( (nextNode != 0) && (ptrPos != 0) ) *ptrPos = parentNode->children.begin() + index; + + } + + if ( (nextNode != 0) && (nextNode->options & kXMP_NewImplicitNode) ) { + nextNode->options |= (nextStep.options & kXMP_PropArrayFormMask); + } + + XMP_Assert ( (ptrPos == 0) || (nextNode == 0) || (nextNode == **ptrPos) ); + XMP_Assert ( (nextNode != 0) || (! createNodes) ); + return nextNode; + +} // FollowXPathStep + +// ------------------------------------------------------------------------------------------------- +// CheckImplicitStruct +// ------------------- + +static inline void +CheckImplicitStruct ( XMP_Node * node, + const XMP_ExpandedXPath & expandedXPath, + size_t stepNum, + size_t stepLim ) +{ + + if ( (stepNum < stepLim) && + ((node->options & kXMP_PropCompositeMask) == 0) && + (GetStepKind ( expandedXPath[stepNum].options ) == kXMP_StructFieldStep) ) { + + node->options |= kXMP_PropValueIsStruct; + + } + +} // CheckImplicitStruct + +// ------------------------------------------------------------------------------------------------- +// DeleteSubtree +// ------------- + +void +DeleteSubtree ( XMP_NodePtrPos rootNodePos ) +{ + XMP_Node * rootNode = *rootNodePos; + XMP_Node * rootParent = rootNode->parent; + + if ( ! (rootNode->options & kXMP_PropIsQualifier) ) { + + rootParent->children.erase ( rootNodePos ); + + } else { + + rootParent->qualifiers.erase ( rootNodePos ); + + XMP_Assert ( rootParent->options & kXMP_PropHasQualifiers); + if ( rootParent->qualifiers.empty() ) rootParent->options ^= kXMP_PropHasQualifiers; + + if ( rootNode->name == "xml:lang" ) { + XMP_Assert ( rootParent->options & kXMP_PropHasLang); + rootParent->options ^= kXMP_PropHasLang; + } else if ( rootNode->name == "rdf:type" ) { + XMP_Assert ( rootParent->options & kXMP_PropHasType); + rootParent->options ^= kXMP_PropHasType; + } + + } + + delete rootNode; + +} // DeleteSubtree + +// ================================================================================================= +// ================================================================================================= + +// ================================================================================================= +// VerifySetOptions +// ================ +// +// Normalize and verify the option flags for SetProperty and similar functions. The allowed options +// here are just those that apply to the property, that would be kept in the XMP_Node. Others that +// affect the selection of the node or other processing must be removed by now. These are: +// kXMP_InsertBeforeItem +// kXMP_InsertAfterItem +// kXMP_KeepQualifiers +// kXMPUtil_AllowCommas + +enum { + kXMP_AllSetOptionsMask = (kXMP_PropValueIsURI | + kXMP_PropValueIsStruct | + kXMP_PropValueIsArray | + kXMP_PropArrayIsOrdered | + kXMP_PropArrayIsAlternate | + kXMP_PropArrayIsAltText | + kXMP_DeleteExisting) +}; + +XMP_OptionBits +VerifySetOptions ( XMP_OptionBits options, XMP_StringPtr propValue ) +{ + + if ( options & kXMP_PropArrayIsAltText ) options |= kXMP_PropArrayIsAlternate; + if ( options & kXMP_PropArrayIsAlternate ) options |= kXMP_PropArrayIsOrdered; + if ( options & kXMP_PropArrayIsOrdered ) options |= kXMP_PropValueIsArray; + + if ( options & ~kXMP_AllSetOptionsMask ) { + XMP_Throw ( "Unrecognized option flags", kXMPErr_BadOptions ); + } + + if ( (options & kXMP_PropValueIsStruct) && (options & kXMP_PropValueIsArray) ) { + XMP_Throw ( "IsStruct and IsArray options are mutually exclusive", kXMPErr_BadOptions ); + } + + if ( (options & kXMP_PropValueOptionsMask) && (options & kXMP_PropCompositeMask) ) { + XMP_Throw ( "Structs and arrays can't have \"value\" options", kXMPErr_BadOptions ); + } + + if ( (propValue != 0) && (options & kXMP_PropCompositeMask) ) { + XMP_Throw ( "Structs and arrays can't have string values", kXMPErr_BadOptions ); + } + + return options; + +} // VerifySetOptions + +// ================================================================================================= +// ComposeXPath +// ============ +// +// Compose the canonical string form of an expanded XPath expression. + +extern void +ComposeXPath ( const XMP_ExpandedXPath & expandedXPath, + XMP_VarString * stringXPath ) +{ + *stringXPath = expandedXPath[kRootPropStep].step; + + for ( size_t index = kRootPropStep+1; index < expandedXPath.size(); ++index ) { + const XPathStepInfo & currStep = expandedXPath[index]; + + switch ( currStep.options & kXMP_StepKindMask ) { + + case kXMP_StructFieldStep : + case kXMP_QualifierStep : + *stringXPath += '/'; + *stringXPath += currStep.step; + break; + + case kXMP_ArrayIndexStep : + case kXMP_ArrayLastStep : + case kXMP_QualSelectorStep : + case kXMP_FieldSelectorStep : + *stringXPath += currStep.step; + break; + + default: + XMP_Throw ( "Unexpected", kXMPErr_InternalFailure ); + + } + + } + +} // ComposeXPath + +// ================================================================================================= +// ExpandXPath +// =========== +// +// Split an XPath expression apart at the conceptual steps, adding the root namespace prefix to the +// first property component. The schema URI is put in the first (0th) slot in the expanded XPath. +// Check if the top level component is an alias, but don't resolve it. +// +// In the most verbose case steps are separated by '/', and each step can be of these forms: +// +// qualName A top level property or struct field. +// *[index] An element of an array. +// *[last()] The last element of an array. +// *[fieldName="value"] An element in an array of structs, chosen by a field value. +// *[@xml:lang="value"] An element in an alt-text array, chosen by the xml:lang qualifier. +// *[?qualName="value"] An element in an array, chosen by a qualifier value. +// @xml:lang An xml:lang qualifier. +// ?qualName A general qualifier. +// +// The logic is complicated though by shorthand for arrays, the separating '/' and leading '*' +// are optional. These are all equivalent: array/*[2] array/[2] array*[2] array[2] +// All of these are broken into the 2 steps "array" and "[2]". +// +// The value portion in the array selector forms is a string quoted by ''' or '"'. The value +// may contain any character including a doubled quoting character. The value may be empty. +// +// The syntax isn't checked, but an XML name begins with a letter or '_', and contains letters, +// digits, '.', '-', '_', and a bunch of special non-ASCII Unicode characters. An XML qualified +// name is a pair of names separated by a colon. + +void +ExpandXPath ( XMP_StringPtr schemaNS, + XMP_StringPtr propPath, + XMP_ExpandedXPath * expandedXPath ) +{ + XMP_Assert ( (schemaNS != 0) && (propPath != 0) && (*propPath != 0) && (expandedXPath != 0) ); + + XMP_StringPtr stepBegin, stepEnd; + XMP_StringPtr qualName, nameEnd; + XMP_VarString currStep; + + size_t resCount = 2; // Guess at the number of steps. At least 2, plus 1 for each '/' or '['. + for ( stepEnd = propPath; *stepEnd != 0; ++stepEnd ) { + if ( (*stepEnd == '/') || (*stepEnd == '[') ) ++resCount; + } + + expandedXPath->clear(); + expandedXPath->reserve ( resCount ); + + // ------------------------------------------------------------------------------------------- + // Pull out the first component and do some special processing on it: add the schema namespace + // prefix and see if it is an alias. The start must be a qualName. + + stepBegin = propPath; + stepEnd = stepBegin; + while ( (*stepEnd != 0) && (*stepEnd != '/') && (*stepEnd != '[') && (*stepEnd != '*') ) ++stepEnd; + if ( stepEnd == stepBegin ) XMP_Throw ( "Empty initial XPath step", kXMPErr_BadXPath ); + currStep.assign ( stepBegin, (stepEnd - stepBegin) ); + + VerifyXPathRoot ( schemaNS, currStep.c_str(), expandedXPath ); + + XMP_OptionBits stepFlags = kXMP_StructFieldStep; + if ( sRegisteredAliasMap->find ( (*expandedXPath)[kRootPropStep].step ) != sRegisteredAliasMap->end() ) { + stepFlags |= kXMP_StepIsAlias; + } + (*expandedXPath)[kRootPropStep].options |= stepFlags; + + // ----------------------------------------------------- + // Now continue to process the rest of the XPath string. + + while ( *stepEnd != 0 ) { + + stepBegin = stepEnd; + if ( *stepBegin == '/' ) ++stepBegin; + if ( *stepBegin == '*' ) { + ++stepBegin; + if ( *stepBegin != '[' ) XMP_Throw ( "Missing '[' after '*'", kXMPErr_BadXPath ); + } + stepEnd = stepBegin; + + if ( *stepBegin != '[' ) { + + // A struct field or qualifier. + qualName = stepBegin; + while ( (*stepEnd != 0) && (*stepEnd != '/') && (*stepEnd != '[') && (*stepEnd != '*') ) ++stepEnd; + nameEnd = stepEnd; + stepFlags = kXMP_StructFieldStep; // ! Touch up later, also changing '@' to '?'. + + } else { + + // One of the array forms. + + ++stepEnd; // Look at the character after the leading '['. + + if ( ('0' <= *stepEnd) && (*stepEnd <= '9') ) { + + // A numeric (decimal integer) array index. + while ( (*stepEnd != 0) && ('0' <= *stepEnd) && (*stepEnd <= '9') ) ++stepEnd; + if ( *stepEnd != ']' ) XMP_Throw ( "Missing ']' for integer array index", kXMPErr_BadXPath ); + stepFlags = kXMP_ArrayIndexStep; + + } else { + + // Could be "[last()]" or one of the selector forms. Find the ']' or '='. + + while ( (*stepEnd != 0) && (*stepEnd != ']') && (*stepEnd != '=') ) ++stepEnd; + if ( *stepEnd == 0 ) XMP_Throw ( "Missing ']' or '=' for array index", kXMPErr_BadXPath ); + + if ( *stepEnd == ']' ) { + + if ( strncmp ( "[last()", stepBegin, (stepEnd - stepBegin) ) != 0 ) { + XMP_Throw ( "Invalid non-numeric array index", kXMPErr_BadXPath ); + } + stepFlags = kXMP_ArrayLastStep; + + } else { + + qualName = stepBegin+1; + nameEnd = stepEnd; + ++stepEnd; // Absorb the '=', remember the quote. + const char quote = *stepEnd; + if ( (quote != '\'') && (quote != '"') ) { + XMP_Throw ( "Invalid quote in array selector", kXMPErr_BadXPath ); + } + + ++stepEnd; // Absorb the leading quote. + while ( *stepEnd != 0 ) { + if ( *stepEnd == quote ) { + if ( *(stepEnd+1) != quote ) break; + ++stepEnd; + } + ++stepEnd; + } + if ( *stepEnd == 0 ) { + XMP_Throw ( "No terminating quote for array selector", kXMPErr_BadXPath ); + } + ++stepEnd; // Absorb the trailing quote. + + stepFlags = kXMP_FieldSelectorStep; // ! Touch up later, also changing '@' to '?'. + + } + + } + + if ( *stepEnd != ']' ) XMP_Throw ( "Missing ']' for array index", kXMPErr_BadXPath ); + ++stepEnd; + + } + + if ( stepEnd == stepBegin ) XMP_Throw ( "Empty XPath step", kXMPErr_BadXPath ); + currStep.assign ( stepBegin, (stepEnd - stepBegin) ); + + if ( GetStepKind ( stepFlags ) == kXMP_StructFieldStep ) { + + if ( currStep[0] == '@' ) { + currStep[0] = '?'; + if ( currStep != "?xml:lang" ) XMP_Throw ( "Only xml:lang allowed with '@'", kXMPErr_BadXPath ); + } + if ( currStep[0] == '?' ) { + ++qualName; + stepFlags = kXMP_QualifierStep; + } + VerifyQualName ( qualName, nameEnd ); + + } else if ( GetStepKind ( stepFlags ) == kXMP_FieldSelectorStep ) { + + if ( currStep[1] == '@' ) { + currStep[1] = '?'; + if ( strncmp ( currStep.c_str(), "[?xml:lang=", 11 ) != 0 ) { + XMP_Throw ( "Only xml:lang allowed with '@'", kXMPErr_BadXPath ); + } + } + if ( currStep[1] == '?' ) { + ++qualName; + stepFlags = kXMP_QualSelectorStep; + } + VerifyQualName ( qualName, nameEnd ); + + } + + expandedXPath->push_back ( XPathStepInfo ( currStep, stepFlags ) ); + + } + +} // ExpandXPath + +// ================================================================================================= +// FindSchemaNode +// ============== +// +// Find or create a schema node. Returns a pointer to the node, and optionally an iterator for the +// node's position in the top level vector of schema nodes. The iterator is unchanged if no schema +// node (null) is returned. + +XMP_Node * +FindSchemaNode ( XMP_Node * xmpTree, + XMP_StringPtr nsURI, + bool createNodes, + XMP_NodePtrPos * ptrPos /* = 0 */ ) +{ + XMP_Node * schemaNode = 0; + + XMP_Assert ( xmpTree->parent == 0 ); + + for ( size_t schemaNum = 0, schemaLim = xmpTree->children.size(); schemaNum != schemaLim; ++schemaNum ) { + XMP_Node * currSchema = xmpTree->children[schemaNum]; + XMP_Assert ( currSchema->parent == xmpTree ); + if ( currSchema->name == nsURI ) { + schemaNode = currSchema; + if ( ptrPos != 0 ) *ptrPos = xmpTree->children.begin() + schemaNum; + break; + } + } + + if ( (schemaNode == 0) && createNodes ) { + + schemaNode = new XMP_Node ( xmpTree, nsURI, (kXMP_SchemaNode | kXMP_NewImplicitNode) ); + + try { + XMP_StringPtr prefixPtr; + XMP_StringLen prefixLen; + bool found = XMPMeta::GetNamespacePrefix ( nsURI, &prefixPtr, &prefixLen ); // *** Use map directly? + XMP_Assert ( found ); + schemaNode->value.assign ( prefixPtr, prefixLen ); + } catch (...) { // Don't leak schemaNode in case of an exception before adding it to the children vector. + delete schemaNode; + throw; + } + + xmpTree->children.push_back ( schemaNode ); + if ( ptrPos != 0 ) *ptrPos = xmpTree->children.end() - 1; + + #if 0 // *** XMP_DebugBuild + schemaNode->_valuePtr = schemaNode->value.c_str(); + #endif + + } + + XMP_Assert ( (ptrPos == 0) || (schemaNode == 0) || (schemaNode == **ptrPos) ); + XMP_Assert ( (schemaNode != 0) || (! createNodes) ); + return schemaNode; + +} // FindSchemaNode + +// ================================================================================================= +// FindChildNode +// ============= +// +// Find or create a child node under a given parent node. Returns a pointer to the child node, and +// optionally an iterator for the node's position in the parent's vector of children. The iterator +// is unchanged if no child node (null) is returned. + +XMP_Node * +FindChildNode ( XMP_Node * parent, + XMP_StringPtr childName, + bool createNodes, + XMP_NodePtrPos * ptrPos /* = 0 */ ) +{ + XMP_Node * childNode = 0; + + if ( ! (parent->options & (kXMP_SchemaNode | kXMP_PropValueIsStruct)) ) { + if ( ! (parent->options & kXMP_NewImplicitNode) ) { + XMP_Throw ( "Named children only allowed for schemas and structs", kXMPErr_BadXPath ); + } + if ( parent->options & kXMP_PropValueIsArray ) { + XMP_Throw ( "Named children not allowed for arrays", kXMPErr_BadXPath ); + } + if ( ! createNodes ) { // *** Should be assert? If !createNodes, why is the parent a new implicit node? + XMP_Throw ( "Parent is new implicit node, but createNodes is false", kXMPErr_InternalFailure ); + } + parent->options |= kXMP_PropValueIsStruct; + } + + for ( size_t childNum = 0, childLim = parent->children.size(); childNum != childLim; ++childNum ) { + XMP_Node * currChild = parent->children[childNum]; + XMP_Assert ( currChild->parent == parent ); + if ( currChild->name == childName ) { + childNode = currChild; + if ( ptrPos != 0 ) *ptrPos = parent->children.begin() + childNum; + break; + } + } + + if ( (childNode == 0) && createNodes ) { + childNode = new XMP_Node ( parent, childName, kXMP_NewImplicitNode ); + parent->children.push_back ( childNode ); + if ( ptrPos != 0 ) *ptrPos = parent->children.end() - 1; + } + + XMP_Assert ( (ptrPos == 0) || (childNode == 0) || (childNode == **ptrPos) ); + XMP_Assert ( (childNode != 0) || (! createNodes) ); + return childNode; + +} // FindChildNode + +// ================================================================================================= +// FindQualifierNode +// ================= +// +// Find or create a qualifier node under a given parent node. Returns a pointer to the qualifier node, +// and optionally an iterator for the node's position in the parent's vector of qualifiers. The iterator +// is unchanged if no qualifier node (null) is returned. +// +// ! On entry, the qualName parameter must not have the leading '?' from the XPath step. + +XMP_Node * +FindQualifierNode ( XMP_Node * parent, + XMP_StringPtr qualName, + bool createNodes, + XMP_NodePtrPos * ptrPos /* = 0 */ ) // *** Require ptrPos internally & remove checks? +{ + XMP_Node * qualNode = 0; + + XMP_Assert ( *qualName != '?' ); + + for ( size_t qualNum = 0, qualLim = parent->qualifiers.size(); qualNum != qualLim; ++qualNum ) { + XMP_Node * currQual = parent->qualifiers[qualNum]; + XMP_Assert ( currQual->parent == parent ); + if ( currQual->name == qualName ) { + qualNode = currQual; + if ( ptrPos != 0 ) *ptrPos = parent->qualifiers.begin() + qualNum; + break; + } + } + + if ( (qualNode == 0) && createNodes ) { + + qualNode = new XMP_Node ( parent, qualName, (kXMP_PropIsQualifier | kXMP_NewImplicitNode) ); + parent->options |= kXMP_PropHasQualifiers; + + const bool isLang = XMP_LitMatch ( qualName, "xml:lang" ); + const bool isType = XMP_LitMatch ( qualName, "rdf:type" ); + const bool isSpecial = isLang | isType; + + if ( isLang ) { + parent->options |= kXMP_PropHasLang; + } else if ( isType ) { + parent->options |= kXMP_PropHasType; + } + + if ( parent->qualifiers.empty() || (! isSpecial) ) { + parent->qualifiers.push_back ( qualNode ); + if ( ptrPos != 0 ) *ptrPos = parent->qualifiers.end() - 1; + } else { + XMP_NodePtrPos insertPos = parent->qualifiers.begin(); // ! Lang goes first, type after. + if ( isType && (parent->options & kXMP_PropHasLang) ) ++insertPos; // *** Does insert at end() work? + insertPos = parent->qualifiers.insert ( insertPos, qualNode ); + if ( ptrPos != 0 ) *ptrPos = insertPos; + } + + } + + XMP_Assert ( (ptrPos == 0) || (qualNode == 0) || (qualNode == **ptrPos) ); + XMP_Assert ( (qualNode != 0) || (! createNodes) ); + return qualNode; + +} // FindQualifierNode + +// ================================================================================================= +// LookupFieldSelector +// =================== +// +// [fieldName="value"] An element in an array of structs, chosen by a field value. +// +// Note that we don't create implicit nodes for field selectors, so no CreateNodes parameter. + +XMP_Index +LookupFieldSelector ( const XMP_Node * arrayNode, XMP_StringPtr fieldName, XMP_StringPtr fieldValue ) +{ + XMP_Index index, itemLim; + + for ( index = 0, itemLim = arrayNode->children.size(); index != itemLim; ++index ) { + + const XMP_Node * currItem = arrayNode->children[index]; + XMP_Assert ( currItem->parent == arrayNode ); + + if ( ! (currItem->options & kXMP_PropValueIsStruct) ) { + XMP_Throw ( "Field selector must be used on array of struct", kXMPErr_BadXPath ); + } + + size_t f, fieldLim; + for ( f = 0, fieldLim = currItem->children.size(); f != fieldLim; ++f ) { + const XMP_Node * currField = currItem->children[f]; + XMP_Assert ( currField->parent == currItem ); + if ( currField->name != fieldName ) continue; + if ( currField->value == fieldValue ) break; // Exit qual loop. + } + if ( f != fieldLim ) break; // Exit child loop, found an item with a matching qualifier. + + } + + if ( index == itemLim ) index = -1; + return index; + +} // LookupFieldSelector + +// ================================================================================================= +// LookupLangItem +// ============== +// +// ! Assumes that the language value is already normalized. + +XMP_Index +LookupLangItem ( const XMP_Node * arrayNode, XMP_VarString & lang ) +{ + if ( ! (arrayNode->options & kXMP_PropValueIsArray) ) { // *** Check for alt-text? + XMP_Throw ( "Language item must be used on array", kXMPErr_BadXPath ); + } + + XMP_Index index = 0; + XMP_Index itemLim = arrayNode->children.size(); + + for ( ; index != itemLim; ++index ) { + const XMP_Node * currItem = arrayNode->children[index]; + XMP_Assert ( currItem->parent == arrayNode ); + if ( currItem->qualifiers.empty() || (currItem->qualifiers[0]->name != "xml:lang") ) continue; + if ( currItem->qualifiers[0]->value == lang ) break; + } + + if ( index == itemLim ) index = -1; + return index; + +} // LookupLangItem + +// ================================================================================================= +// FindNode +// ======== +// +// Follow an expanded path expression to find or create a node. Returns a pointer to the node, and +// optionally an iterator for the node's position in the parent's vector of children or qualifiers. +// The iterator is unchanged if no child node (null) is returned. + +XMP_Node * +FindNode ( XMP_Node * xmpTree, + const XMP_ExpandedXPath & expandedXPath, + bool createNodes, + XMP_OptionBits leafOptions /* = 0 */, + XMP_NodePtrPos * ptrPos /* = 0 */ ) +{ + XMP_Node * currNode = 0; + XMP_NodePtrPos currPos; + XMP_NodePtrPos newSubPos; // Root of implicitly created subtree. Valid only if leaf is new. + bool leafIsNew = false; + + XMP_Assert ( (leafOptions == 0) || createNodes ); + + if ( expandedXPath.empty() ) XMP_Throw ( "Empty XPath", kXMPErr_BadXPath ); + + size_t stepNum = 1; // By default start calling FollowXPathStep for the top level property step. + size_t stepLim = expandedXPath.size(); + + // The start of processing deals with the schema node and top level alias. If the top level step + // is not an alias, lookup the expanded path's schema URI. Otherwise, lookup the expanded path + // for the actual. While tempting, don't substitute the actual's path into the local one, don't + // risk messing with the caller's use of that. Also don't call FindNode recursively, we need to + // keep track of the root of the implicitly created subtree as we move down the path. + + if ( ! (expandedXPath[kRootPropStep].options & kXMP_StepIsAlias) ) { + + currNode = FindSchemaNode ( xmpTree, expandedXPath[kSchemaStep].step.c_str(), createNodes, &currPos ); + if ( currNode == 0 ) return 0; + + if ( currNode->options & kXMP_NewImplicitNode ) { + currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit. + if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node. + leafIsNew = true; // If any parent is new, the leaf will be new also. + } + + } else { + + stepNum = 2; // ! Continue processing the original path at the second level step. + + XMP_AliasMapPos aliasPos = sRegisteredAliasMap->find ( expandedXPath[kRootPropStep].step ); + XMP_Assert ( aliasPos != sRegisteredAliasMap->end() ); + + currNode = FindSchemaNode ( xmpTree, aliasPos->second[kSchemaStep].step.c_str(), createNodes, &currPos ); + if ( currNode == 0 ) goto EXIT; + if ( currNode->options & kXMP_NewImplicitNode ) { + currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit. + if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node. + leafIsNew = true; // If any parent is new, the leaf will be new also. + } + + currNode = FollowXPathStep ( currNode, aliasPos->second, 1, createNodes, &currPos ); + if ( currNode == 0 ) goto EXIT; + if ( currNode->options & kXMP_NewImplicitNode ) { + currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit. + CheckImplicitStruct ( currNode, expandedXPath, 2, stepLim ); + if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node. + leafIsNew = true; // If any parent is new, the leaf will be new also. + } + + XMP_OptionBits arrayForm = aliasPos->second[kRootPropStep].options & kXMP_PropArrayFormMask; + XMP_Assert ( (arrayForm == 0) || (arrayForm & kXMP_PropValueIsArray) ); + XMP_Assert ( (arrayForm == 0) ? (aliasPos->second.size() == 2) : (aliasPos->second.size() == 3) ); + + if ( arrayForm != 0 ) { + currNode = FollowXPathStep ( currNode, aliasPos->second, 2, createNodes, &currPos, true ); + if ( currNode == 0 ) goto EXIT; + if ( currNode->options & kXMP_NewImplicitNode ) { + currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit. + CheckImplicitStruct ( currNode, expandedXPath, 2, stepLim ); + if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node. + leafIsNew = true; // If any parent is new, the leaf will be new also. + } + } + + } + + // Now follow the remaining steps of the original XPath. + + // *** ??? Change all the num/lim loops back to numoptions & kXMP_NewImplicitNode ) { + currNode->options ^= kXMP_NewImplicitNode; // Clear the implicit node bit. + CheckImplicitStruct ( currNode, expandedXPath, stepNum+1, stepLim ); + if ( ! leafIsNew ) newSubPos = currPos; // Save the top most implicit node. + leafIsNew = true; // If any parent is new, the leaf will be new also. + } + } + } catch ( ... ) { + if ( leafIsNew ) DeleteSubtree ( newSubPos ); + throw; + } + + // Done. Delete the implicitly created subtree if the eventual node was not found. + +EXIT: + + XMP_Assert ( (currNode == 0) || (currNode == *currPos) ); + XMP_Assert ( (currNode != 0) || (! createNodes) ); + + if ( leafIsNew ) { + if ( currNode != 0 ) { + currNode->options |= leafOptions; + } else { + DeleteSubtree ( newSubPos ); + } + } + + if ( (currNode != 0) && (ptrPos != 0) ) *ptrPos = currPos; + return currNode; + +} // FindNode + +// ================================================================================================= +// CloneOffspring +// ============== + +void +CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent, bool skipEmpty /* = false */ ) +{ + size_t qualCount = origParent->qualifiers.size(); + size_t childCount = origParent->children.size(); + + if ( qualCount > 0 ) { + + cloneParent->qualifiers.reserve ( qualCount ); + + for ( size_t qualNum = 0, qualLim = qualCount; qualNum != qualLim; ++qualNum ) { + const XMP_Node * origQual = origParent->qualifiers[qualNum]; + if ( skipEmpty && origQual->value.empty() && origQual->children.empty() ) continue; + XMP_Node * cloneQual = new XMP_Node ( cloneParent, origQual->name, origQual->value, origQual->options ); + CloneOffspring ( origQual, cloneQual, skipEmpty ); + if ( skipEmpty && cloneQual->value.empty() && cloneQual->children.empty() ) { + // Check again, might have had an array or struct with all empty children. + delete cloneQual; + continue; + } + cloneParent->qualifiers.push_back ( cloneQual ); + } + + } + + if ( childCount > 0 ) { + + cloneParent->children.reserve ( childCount ); + + for ( size_t childNum = 0, childLim = childCount; childNum != childLim; ++childNum ) { + const XMP_Node * origChild = origParent->children[childNum]; + if ( skipEmpty && origChild->value.empty() && origChild->children.empty() ) continue; + XMP_Node * cloneChild = new XMP_Node ( cloneParent, origChild->name, origChild->value, origChild->options ); + CloneOffspring ( origChild, cloneChild, skipEmpty ); + if ( skipEmpty && cloneChild->value.empty() && cloneChild->children.empty() ) { + // Check again, might have had an array or struct with all empty children. + delete cloneChild; + continue; + } + cloneParent->children.push_back ( cloneChild ); + } + + } + +} // CloneOffspring + +// ================================================================================================= +// CloneSubtree +// ============ + +XMP_Node * +CloneSubtree ( const XMP_Node * origRoot, XMP_Node * cloneParent, bool skipEmpty /* = false */ ) +{ + #if XMP_DebugBuild + if ( cloneParent->parent == 0 ) { + XMP_Assert ( origRoot->options & kXMP_SchemaNode ); + XMP_Assert ( FindConstSchema ( cloneParent, origRoot->name.c_str() ) == 0 ); + } else { + XMP_Assert ( ! (origRoot->options & kXMP_SchemaNode) ); + if ( cloneParent->options & kXMP_PropValueIsStruct ) { // Might be an array. + XMP_Assert ( FindConstChild ( cloneParent, origRoot->name.c_str() ) == 0 ); + } + } + #endif + + XMP_Node * cloneRoot = new XMP_Node ( cloneParent, origRoot->name, origRoot->value, origRoot->options ); + CloneOffspring ( origRoot, cloneRoot, skipEmpty ) ; + + if ( skipEmpty && cloneRoot->value.empty() && cloneRoot->children.empty() ) { + // ! Can't do earlier, CloneOffspring might be skipping empty children. + delete cloneRoot; + return 0; + } + + cloneParent->children.push_back ( cloneRoot ); + return cloneRoot; + +} // CloneSubtree + +// ================================================================================================= +// CompareSubtrees +// =============== +// +// Compare 2 subtrees for semantic equality. The comparison includes value, qualifiers, and form. +// Schemas, top level properties, struct fields, and qualifiers are allowed to have differing order, +// the appropriate right node is found from the left node's name. Alt-text arrays are allowed to be +// in differing language order, other arrays are compared in order. + +// *** Might someday consider sorting unordered arrays. +// *** Should expose this through XMPUtils. + +bool +CompareSubtrees ( const XMP_Node & leftNode, const XMP_Node & rightNode ) +{ + // Don't compare the names here, we want to allow the outermost roots to have different names. + if ( (leftNode.value != rightNode.value) || + (leftNode.options != rightNode.options) || + (leftNode.children.size() != rightNode.children.size()) || + (leftNode.qualifiers.size() != rightNode.qualifiers.size()) ) return false; + + // Compare the qualifiers, allowing them to be out of order. + for ( size_t qualNum = 0, qualLim = leftNode.qualifiers.size(); qualNum != qualLim; ++qualNum ) { + const XMP_Node * leftQual = leftNode.qualifiers[qualNum]; + const XMP_Node * rightQual = FindConstQualifier ( &rightNode, leftQual->name.c_str() ); + if ( (rightQual == 0) || (! CompareSubtrees ( *leftQual, *rightQual )) ) return false; + } + + if ( (leftNode.parent == 0) || (leftNode.options & (kXMP_SchemaNode | kXMP_PropValueIsStruct)) ) { + + // The parent node is a tree root, a schema, or a struct. + for ( size_t childNum = 0, childLim = leftNode.children.size(); childNum != childLim; ++childNum ) { + const XMP_Node * leftChild = leftNode.children[childNum]; + const XMP_Node * rightChild = FindConstChild ( &rightNode, leftChild->name.c_str() ); + if ( (rightChild == 0) || (! CompareSubtrees ( *leftChild, *rightChild )) ) return false; + } + + } else if ( leftNode.options & kXMP_PropArrayIsAltText ) { + + // The parent node is an alt-text array. + for ( size_t childNum = 0, childLim = leftNode.children.size(); childNum != childLim; ++childNum ) { + const XMP_Node * leftChild = leftNode.children[childNum]; + XMP_Assert ( (! leftChild->qualifiers.empty()) && (leftChild->qualifiers[0]->name == "xml:lang") ); + XMP_Index rightIndex = LookupLangItem ( &rightNode, leftChild->qualifiers[0]->value ); + if ( rightIndex == -1 ) return false; + const XMP_Node * rightChild = rightNode.children[rightIndex]; + if ( ! CompareSubtrees ( *leftChild, *rightChild ) ) return false; + } + + } else { + + // The parent must be simple or some other (not alt-text) kind of array. + XMP_Assert ( (! (leftNode.options & kXMP_PropCompositeMask)) || (leftNode.options & kXMP_PropValueIsArray) ); + for ( size_t childNum = 0, childLim = leftNode.children.size(); childNum != childLim; ++childNum ) { + const XMP_Node * leftChild = leftNode.children[childNum]; + const XMP_Node * rightChild = rightNode.children[childNum]; + if ( ! CompareSubtrees ( *leftChild, *rightChild ) ) return false; + } + + } + + return true; + +} // CompareSubtrees + +// ================================================================================================= +// DeleteEmptySchema +// ================= + +void +DeleteEmptySchema ( XMP_Node * schemaNode ) +{ + + if ( XMP_NodeIsSchema ( schemaNode->options ) && schemaNode->children.empty() ) { + + XMP_Node * xmpTree = schemaNode->parent; + + size_t schemaNum = 0; + size_t schemaLim = xmpTree->children.size(); + while ( (schemaNum < schemaLim) && (xmpTree->children[schemaNum] != schemaNode) ) ++schemaNum; + XMP_Assert ( schemaNum < schemaLim ); + + XMP_NodePtrPos schemaPos = xmpTree->children.begin() + schemaNum; + XMP_Assert ( *schemaPos == schemaNode ); + + xmpTree->children.erase ( schemaPos ); + delete schemaNode; + + } + +} // DeleteEmptySchema + +// ================================================================================================= +// NormalizeLangValue +// ================== +// +// Normalize an xml:lang value so that comparisons are effectively case insensitive as required by +// RFC 3066 (which superceeds RFC 1766). The normalization rules: +// +// - The primary subtag is lower case, the suggested practice of ISO 639. +// - All 2 letter secondary subtags are upper case, the suggested practice of ISO 3166. +// - All other subtags are lower case. + +void +NormalizeLangValue ( XMP_VarString * value ) +{ + char * tagStart; + char * tagEnd; + + // Find and process the primary subtag. + + tagStart = (char*) value->c_str(); + for ( tagEnd = tagStart; (*tagEnd != 0) && (*tagEnd != '-'); ++tagEnd ) { + if ( ('A' <= *tagEnd) && (*tagEnd <= 'Z') ) *tagEnd += 0x20; + } + + // Find and process the secondary subtag. + + tagStart = tagEnd; + if ( *tagStart == '-' ) ++tagStart; + for ( tagEnd = tagStart; (*tagEnd != 0) && (*tagEnd != '-'); ++tagEnd ) { + if ( ('A' <= *tagEnd) && (*tagEnd <= 'Z') ) *tagEnd += 0x20; + } + if ( tagEnd == tagStart+2 ) { + if ( ('a' <= *tagStart) && (*tagStart <= 'z') ) *tagStart -= 0x20; + ++tagStart; + if ( ('a' <= *tagStart) && (*tagStart <= 'z') ) *tagStart -= 0x20; + } + + // Find and process the remaining subtags. + + while ( true ) { + tagStart = tagEnd; + if ( *tagStart == '-' ) ++tagStart; + if ( *tagStart == 0 ) break; + for ( tagEnd = tagStart; (*tagEnd != 0) && (*tagEnd != '-'); ++tagEnd ) { + if ( ('A' <= *tagEnd) && (*tagEnd <= 'Z') ) *tagEnd += 0x20; + } + } + +} // NormalizeLangValue + +// ================================================================================================= +// NormalizeLangArray +// ================== +// +// Make sure the x-default item is first. Touch up "single value" arrays that have a default plus +// one real language. This case should have the same value for both items. Older Adobe apps were +// hardwired to only use the 'x-default' item, so we copy that value to the other item. + +void +NormalizeLangArray ( XMP_Node * array ) +{ + XMP_Assert ( XMP_ArrayIsAltText(array->options) ); + + size_t itemNum; + size_t itemLim = array->children.size(); + bool hasDefault = false; + + for ( itemNum = 0; itemNum < itemLim; ++itemNum ) { + + if ( array->children[itemNum]->qualifiers.empty() || + (array->children[itemNum]->qualifiers[0]->name != "xml:lang") ) { + XMP_Throw ( "AltText array items must have an xml:lang qualifier", kXMPErr_BadXMP ); + } + + if ( array->children[itemNum]->qualifiers[0]->value == "x-default" ) { + hasDefault = true; + break; + } + + } + + if ( hasDefault ) { + + if ( itemNum != 0 ) { + XMP_Node * temp = array->children[0]; + array->children[0] = array->children[itemNum]; + array->children[itemNum] = temp; + } + + if ( itemLim == 2 ) array->children[1]->value = array->children[0]->value; + + } + +} // NormalizeLangArray + +// ================================================================================================= +// DetectAltText +// ============= +// +// See if an array is an alt-text array. If so, make sure the x-default item is first. + +void +DetectAltText ( XMP_Node * xmpParent ) +{ + XMP_Assert ( XMP_ArrayIsAlternate(xmpParent->options) ); + + size_t itemNum, itemLim; + + for ( itemNum = 0, itemLim = xmpParent->children.size(); itemNum < itemLim; ++itemNum ) { + XMP_OptionBits currOptions = xmpParent->children[itemNum]->options; + if ( (currOptions & kXMP_PropCompositeMask) || (! (currOptions & kXMP_PropHasLang)) ) break; + } + + if ( (itemLim != 0) && (itemNum == itemLim) ) { + xmpParent->options |= kXMP_PropArrayIsAltText; + NormalizeLangArray ( xmpParent ); + } + +} // DetectAltText + +// ================================================================================================= +// SortNamedNodes +// ============== +// +// Sort the pointers in an XMP_NodeOffspring vector by name. + +static inline bool Compare ( const XMP_Node * left, const XMP_Node * right ) +{ + return (left->name < right->name); +} + +void +SortNamedNodes ( XMP_NodeOffspring & nodeVector ) +{ + sort ( nodeVector.begin(), nodeVector.end(), Compare ); +} // SortNamedNodes + +// ================================================================================================= diff --git a/source/lib/xmp_core/XMPCore_Impl.hpp b/source/lib/xmp_core/XMPCore_Impl.hpp new file mode 100644 index 0000000..d900ef8 --- /dev/null +++ b/source/lib/xmp_core/XMPCore_Impl.hpp @@ -0,0 +1,392 @@ +#ifndef __XMPCore_Impl_hpp__ +#define __XMPCore_Impl_hpp__ 1 + +// ================================================================================================= +// Copyright 2004 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! Must be the first #include! +#include "public/include/XMP_Const.h" +#include "XMP_BuildInfo.h" +#include "XMP_LibUtils.hpp" + +// #include "XMPCore/source/XMPMeta.hpp" + +#include "public/include/client-glue/WXMP_Common.hpp" + +#include +#include +#include +#include +#include +#include + +#if XMP_WinBuild + #pragma warning ( disable : 4244 ) // possible loss of data (temporary for 64 bit builds) + #pragma warning ( disable : 4267 ) // possible loss of data (temporary for 64 bit builds) +#endif + +// ================================================================================================= +// Primary internal types + +class XMP_Node; +class XML_Node; +class XPathStepInfo; + +typedef XMP_Node * XMP_NodePtr; + +typedef std::vector XMP_NodeOffspring; +typedef XMP_NodeOffspring::iterator XMP_NodePtrPos; + +typedef XMP_VarString::iterator XMP_VarStringPos; +typedef XMP_VarString::const_iterator XMP_cVarStringPos; + +typedef std::vector < XPathStepInfo > XMP_ExpandedXPath; +typedef XMP_ExpandedXPath::iterator XMP_ExpandedXPathPos; +typedef XMP_ExpandedXPath::const_iterator XMP_cExpandedXPathPos; + +typedef std::map < XMP_VarString, XMP_ExpandedXPath > XMP_AliasMap; // Alias name to actual path. +typedef XMP_AliasMap::iterator XMP_AliasMapPos; +typedef XMP_AliasMap::const_iterator XMP_cAliasMapPos; + +// ================================================================================================= +// General global variables and macros + +extern XMP_Int32 sXMP_InitCount; + +extern XMP_NamespaceTable * sRegisteredNamespaces; + +extern XMP_AliasMap * sRegisteredAliasMap; + +#define WtoXMPMeta_Ref(xmpRef) (const XMPMeta &) (*((XMPMeta*)(xmpRef))) +#define WtoXMPMeta_Ptr(xmpRef) ((XMPMeta*)(xmpRef)) + +#define WtoXMPDocOps_Ptr(docRef) ((XMPDocOps*)(docRef)) + +extern void * voidVoidPtr; // Used to backfill null output parameters. +extern XMP_StringPtr voidStringPtr; +extern XMP_StringLen voidStringLen; +extern XMP_OptionBits voidOptionBits; +extern XMP_Bool voidByte; +extern bool voidBool; +extern XMP_Int32 voidInt32; +extern XMP_Int64 voidInt64; +extern double voidDouble; +extern XMP_DateTime voidDateTime; +extern WXMP_Result void_wResult; + +#define kHexDigits "0123456789ABCDEF" + +#define XMP_LitMatch(s,l) (strcmp((s),(l)) == 0) +#define XMP_LitNMatch(s,l,n) (strncmp((s),(l),(n)) == 0) + // *** Use the above macros! + +#if XMP_WinBuild + #define snprintf _snprintf +#endif + +// ================================================================================================= +// Version info + +#if XMP_DebugBuild + #define kXMPCore_DebugFlag 1 +#else + #define kXMPCore_DebugFlag 0 +#endif + +#define kXMPCore_VersionNumber ( (kXMPCore_DebugFlag << 31) | \ + (XMP_API_VERSION_MAJOR << 24) | \ + (XMP_API_VERSION_MINOR << 16) | \ + (XMP_API_VERSION_MICRO << 8) ) + + #define kXMPCoreName "XMP Core" + #define kXMPCore_VersionMessage kXMPCoreName " " XMPCORE_API_VERSION_STRING +// ================================================================================================= +// Support for call tracing + +#ifndef XMP_TraceCoreCalls + #define XMP_TraceCoreCalls 0 + #define XMP_TraceCoreCallsToFile 0 +#endif + +#if XMP_TraceCoreCalls + + #undef AnnounceThrow + #undef AnnounceCatch + + #undef AnnounceEntry + #undef AnnounceNoLock + #undef AnnounceExit + + extern FILE * xmpCoreLog; + + #define AnnounceThrow(msg) \ + fprintf ( xmpCoreLog, "XMP_Throw: %s\n", msg ); fflush ( xmpCoreLog ) + #define AnnounceCatch(msg) \ + fprintf ( xmpCoreLog, "Catch in %s: %s\n", procName, msg ); fflush ( xmpCoreLog ) + + #define AnnounceEntry(proc) \ + const char * procName = proc; \ + fprintf ( xmpCoreLog, "Entering %s\n", procName ); fflush ( xmpCoreLog ) + #define AnnounceNoLock(proc) \ + const char * procName = proc; \ + fprintf ( xmpCoreLog, "Entering %s (no lock)\n", procName ); fflush ( xmpCoreLog ) + #define AnnounceExit() \ + fprintf ( xmpCoreLog, "Exiting %s\n", procName ); fflush ( xmpCoreLog ) + +#endif + +// ================================================================================================= +// ExpandXPath, FindNode, and related support + +// *** Normalize the use of "const xx &" for input params + +#define kXMP_ArrayItemName "[]" + +#define kXMP_CreateNodes true +#define kXMP_ExistingOnly false + +#define FindConstSchema(t,u) FindSchemaNode ( const_cast(t), u, kXMP_ExistingOnly, 0 ) +#define FindConstChild(p,c) FindChildNode ( const_cast(p), c, kXMP_ExistingOnly, 0 ) +#define FindConstQualifier(p,c) FindQualifierNode ( const_cast(p), c, kXMP_ExistingOnly, 0 ) +#define FindConstNode(t,p) FindNode ( const_cast(t), p, kXMP_ExistingOnly, 0 ) + +extern XMP_OptionBits +VerifySetOptions ( XMP_OptionBits options, XMP_StringPtr propValue ); + +extern void +ComposeXPath ( const XMP_ExpandedXPath & expandedXPath, + XMP_VarString * stringXPath ); + +extern void +ExpandXPath ( XMP_StringPtr schemaNS, + XMP_StringPtr propPath, + XMP_ExpandedXPath * expandedXPath ); + +extern XMP_Node * +FindSchemaNode ( XMP_Node * xmpTree, + XMP_StringPtr nsURI, + bool createNodes, + XMP_NodePtrPos * ptrPos = 0 ); + +extern XMP_Node * +FindChildNode ( XMP_Node * parent, + XMP_StringPtr childName, + bool createNodes, + XMP_NodePtrPos * ptrPos = 0 ); + +extern XMP_Node * +FindQualifierNode ( XMP_Node * parent, + XMP_StringPtr qualName, + bool createNodes, + XMP_NodePtrPos * ptrPos = 0 ); + +extern XMP_Node * +FindNode ( XMP_Node * xmpTree, + const XMP_ExpandedXPath & expandedXPath, + bool createNodes, + XMP_OptionBits leafOptions = 0, + XMP_NodePtrPos * ptrPos = 0 ); + +extern XMP_Index +LookupLangItem ( const XMP_Node * arrayNode, XMP_VarString & lang ); // ! Lang must be normalized! + +extern XMP_Index +LookupFieldSelector ( const XMP_Node * arrayNode, XMP_StringPtr fieldName, XMP_StringPtr fieldValue ); + +extern void +CloneOffspring ( const XMP_Node * origParent, XMP_Node * cloneParent, bool skipEmpty = false ); + +extern XMP_Node * +CloneSubtree ( const XMP_Node * origRoot, XMP_Node * cloneParent, bool skipEmpty = false ); + +extern bool +CompareSubtrees ( const XMP_Node & leftNode, const XMP_Node & rightNode ); + +extern void +DeleteSubtree ( XMP_NodePtrPos rootNodePos ); + +extern void +DeleteEmptySchema ( XMP_Node * schemaNode ); + +extern void +NormalizeLangValue ( XMP_VarString * value ); + +extern void +NormalizeLangArray ( XMP_Node * array ); + +extern void +DetectAltText ( XMP_Node * xmpParent ); + +extern void +SortNamedNodes ( XMP_NodeOffspring & nodeVector ); + +static inline bool +IsPathPrefix ( XMP_StringPtr fullPath, XMP_StringPtr prefix ) +{ + bool isPrefix = false; + XMP_StringLen prefixLen = strlen(prefix); + if ( XMP_LitNMatch ( prefix, fullPath, prefixLen ) ) { + char separator = fullPath[prefixLen]; + if ( (separator == 0) || (separator == '/') || + (separator == '[') || (separator == '*') ) isPrefix = true; + } + return isPrefix; +} + +// ------------------------------------------------------------------------------------------------- + +class XPathStepInfo { +public: + XMP_VarString step; + XMP_OptionBits options; + XPathStepInfo ( XMP_StringPtr _step, XMP_OptionBits _options ) : step(_step), options(_options) {}; + XPathStepInfo ( XMP_VarString _step, XMP_OptionBits _options ) : step(_step), options(_options) {}; +private: + XPathStepInfo() : options(0) {}; // ! Hide the default constructor. +}; + +enum { kSchemaStep = 0, kRootPropStep = 1, kAliasIndexStep = 2 }; + +enum { // Bits for XPathStepInfo options. // *** Add mask check to init code. + kXMP_StepKindMask = 0x0F, // ! The step kinds are mutually exclusive numbers. + kXMP_StructFieldStep = 0x01, // Also for top level nodes (schema "fields"). + kXMP_QualifierStep = 0x02, // ! Order is significant to separate struct/qual from array kinds! + kXMP_ArrayIndexStep = 0x03, // ! The kinds must not overlay array form bits! + kXMP_ArrayLastStep = 0x04, + kXMP_QualSelectorStep = 0x05, + kXMP_FieldSelectorStep = 0x06, + kXMP_StepIsAlias = 0x10 +}; + +#define GetStepKind(f) ((f) & kXMP_StepKindMask) + +#define kXMP_NewImplicitNode kXMP_InsertAfterItem + +// ================================================================================================= +// XMP_Node details + +#if 0 // Pattern for iterating over the children or qualifiers: + for ( size_t xxNum = 0, xxLim = _node_->_offspring_.size(); xxNum < xxLim; ++xxNum ) { + const XMP_Node * _curr_ = _node_->_offspring_[xxNum]; + } +#endif + +class XMP_Node { +public: + + XMP_OptionBits options; + XMP_VarString name, value; + XMP_Node * parent; + XMP_NodeOffspring children; + XMP_NodeOffspring qualifiers; + #if XMP_DebugBuild + // *** XMP_StringPtr _namePtr, _valuePtr; // *** Not working, need operator=? + #endif + + XMP_Node ( XMP_Node * _parent, XMP_StringPtr _name, XMP_OptionBits _options ) + : options(_options), name(_name), parent(_parent) + { + #if XMP_DebugBuild + XMP_Assert ( (name.find ( ':' ) != XMP_VarString::npos) || (name == kXMP_ArrayItemName) || + (options & kXMP_SchemaNode) || (parent == 0) ); + // *** _namePtr = name.c_str(); + // *** _valuePtr = value.c_str(); + #endif + }; + + XMP_Node ( XMP_Node * _parent, const XMP_VarString & _name, XMP_OptionBits _options ) + : options(_options), name(_name), parent(_parent) + { + #if XMP_DebugBuild + XMP_Assert ( (name.find ( ':' ) != XMP_VarString::npos) || (name == kXMP_ArrayItemName) || + (options & kXMP_SchemaNode) || (parent == 0) ); + // *** _namePtr = name.c_str(); + // *** _valuePtr = value.c_str(); + #endif + }; + + XMP_Node ( XMP_Node * _parent, XMP_StringPtr _name, XMP_StringPtr _value, XMP_OptionBits _options ) + : options(_options), name(_name), value(_value), parent(_parent) + { + #if XMP_DebugBuild + XMP_Assert ( (name.find ( ':' ) != XMP_VarString::npos) || (name == kXMP_ArrayItemName) || + (options & kXMP_SchemaNode) || (parent == 0) ); + // *** _namePtr = name.c_str(); + // *** _valuePtr = value.c_str(); + #endif + }; + + XMP_Node ( XMP_Node * _parent, const XMP_VarString & _name, const XMP_VarString & _value, XMP_OptionBits _options ) + : options(_options), name(_name), value(_value), parent(_parent) + { + #if XMP_DebugBuild + XMP_Assert ( (name.find ( ':' ) != XMP_VarString::npos) || (name == kXMP_ArrayItemName) || + (options & kXMP_SchemaNode) || (parent == 0) ); + // *** _namePtr = name.c_str(); + // *** _valuePtr = value.c_str(); + #endif + }; + + void GetLocalURI ( XMP_StringPtr * uriStr, XMP_StringLen * uriSize ) const; + + void RemoveChildren() + { + for ( size_t i = 0, vLim = children.size(); i < vLim; ++i ) { + if ( children[i] != 0 ) delete children[i]; + } + children.clear(); + } + + void RemoveQualifiers() + { + for ( size_t i = 0, vLim = qualifiers.size(); i < vLim; ++i ) { + if ( qualifiers[i] != 0 ) delete qualifiers[i]; + } + qualifiers.clear(); + } + + void ClearNode() + { + options = 0; + name.erase(); + value.erase(); + this->RemoveChildren(); + this->RemoveQualifiers(); + } + + virtual ~XMP_Node() { RemoveChildren(); RemoveQualifiers(); }; + +private: + XMP_Node() : options(0), parent(0) // ! Make sure parent pointer is always set. + { + #if XMP_DebugBuild + // *** _namePtr = name.c_str(); + // *** _valuePtr = value.c_str(); + #endif + }; + +}; + +class XMP_AutoNode { // Used to hold a child during subtree construction. +public: + XMP_Node * nodePtr; + XMP_AutoNode() : nodePtr(0) {}; + ~XMP_AutoNode() { if ( nodePtr != 0 ) delete ( nodePtr ); nodePtr = 0; }; + XMP_AutoNode ( XMP_Node * _parent, XMP_StringPtr _name, XMP_OptionBits _options ) + : nodePtr ( new XMP_Node ( _parent, _name, _options ) ) {}; + XMP_AutoNode ( XMP_Node * _parent, const XMP_VarString & _name, XMP_OptionBits _options ) + : nodePtr ( new XMP_Node ( _parent, _name, _options ) ) {}; + XMP_AutoNode ( XMP_Node * _parent, XMP_StringPtr _name, XMP_StringPtr _value, XMP_OptionBits _options ) + : nodePtr ( new XMP_Node ( _parent, _name, _value, _options ) ) {}; + XMP_AutoNode ( XMP_Node * _parent, const XMP_VarString & _name, const XMP_VarString & _value, XMP_OptionBits _options ) + : nodePtr ( new XMP_Node ( _parent, _name, _value, _options ) ) {}; +}; + +// ================================================================================================= + +#endif // __XMPCore_Impl_hpp__ diff --git a/source/lib/xmp_core/XMPIterator.cpp b/source/lib/xmp_core/XMPIterator.cpp new file mode 100644 index 0000000..229cd8f --- /dev/null +++ b/source/lib/xmp_core/XMPIterator.cpp @@ -0,0 +1,637 @@ +// ================================================================================================= +// Copyright 2003 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! This must be the first include! +#include "XMPCore_Impl.hpp" + +#include "XMPIterator.hpp" + +#include +#include // For snprintf. + +#if XMP_WinBuild + #pragma warning ( disable : 4702 ) // unreachable code + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) + #pragma warning ( disable : 4996 ) // '...' was declared deprecated +#endif + +// ================================================================================================= +// Support Routines +// ================================================================================================= + + +#ifndef TraceIterators + #define TraceIterators 0 +#endif + +#if TraceIterators + static const char * sStageNames[] = { "before", "self", "qualifiers", "children" }; +#endif + +static XMP_Node * sDummySchema = 0; // ! Used for some ugliness with aliases. + +// ------------------------------------------------------------------------------------------------- +// AddSchemaProps +// -------------- +// +// Add the top level properties to the IterNode for a schema. + +static void +AddSchemaProps ( IterInfo & info, IterNode & iterSchema, const XMP_Node * xmpSchema ) +{ + #if TraceIterators + printf ( " Adding properties of %s\n", xmpSchema->name.c_str() ); + #endif + + for ( size_t propNum = 0, propLim = xmpSchema->children.size(); propNum != propLim; ++propNum ) { + const XMP_Node * xmpProp = xmpSchema->children[propNum]; + // *** set the has-aliases bit when appropriate + iterSchema.children.push_back ( IterNode ( xmpProp->options, xmpProp->name, 0 ) ); + #if TraceIterators + printf ( " %s\n", xmpProp->name.c_str() ); + #endif + } + +} // AddSchemaProps + +// ------------------------------------------------------------------------------------------------- +// AddNodeOffspring +// ---------------- +// +// Add the immediate children and qualifiers to an IterNode. + +static void +AddNodeOffspring ( IterInfo & info, IterNode & iterParent, const XMP_Node * xmpParent ) +{ + XMP_VarString currPath ( iterParent.fullPath ); + size_t leafOffset = iterParent.fullPath.size(); + + if ( (! xmpParent->qualifiers.empty()) && (! (info.options & kXMP_IterOmitQualifiers)) ) { + + #if TraceIterators + printf ( " Adding qualifiers of %s\n", currPath.c_str() ); + #endif + + currPath += "/?"; // All qualifiers are named and use paths like "Prop/?Qual". + leafOffset += 2; + + for ( size_t qualNum = 0, qualLim = xmpParent->qualifiers.size(); qualNum != qualLim; ++qualNum ) { + const XMP_Node * xmpQual = xmpParent->qualifiers[qualNum]; + currPath += xmpQual->name; + iterParent.qualifiers.push_back ( IterNode ( xmpQual->options, currPath, leafOffset ) ); + currPath.erase ( leafOffset ); + #if TraceIterators + printf ( " %s\n", xmpQual->name.c_str() ); + #endif + } + + leafOffset -= 2; + currPath.erase ( leafOffset ); + + } + + if ( ! xmpParent->children.empty() ) { + + #if TraceIterators + printf ( " Adding children of %s\n", currPath.c_str() ); + #endif + + XMP_Assert ( xmpParent->options & kXMP_PropCompositeMask ); + + if ( xmpParent->options & kXMP_PropValueIsStruct ) { + currPath += '/'; + leafOffset += 1; + } + + for ( size_t childNum = 0, childLim = xmpParent->children.size(); childNum != childLim; ++childNum ) { + const XMP_Node * xmpChild = xmpParent->children[childNum]; + if ( ! (xmpParent->options & kXMP_PropValueIsArray) ) { + currPath += xmpChild->name; + } else { + char buffer [32]; // AUDIT: Using sizeof(buffer) below for snprintf length is safe. + snprintf ( buffer, sizeof(buffer), "[%lu]", childNum+1 ); // ! XPath indices are one-based. + currPath += buffer; + } + iterParent.children.push_back ( IterNode ( xmpChild->options, currPath, leafOffset ) ); + currPath.erase ( leafOffset ); + #if TraceIterators + printf ( " %s\n", (iterParent.children.back().fullPath.c_str() + leafOffset) ); + #endif + } + + } + +} // AddNodeOffspring + +// ------------------------------------------------------------------------------------------------- +// SetCurrSchema +// ------------- + +static inline void +SetCurrSchema ( IterInfo & info, XMP_StringPtr schemaName ) +{ + + info.currSchema = schemaName; + #if 0 // *** XMP_DebugBuild + info._schemaPtr = info.currSchema.c_str(); + #endif + +} // SetCurrSchema + +static inline void +SetCurrSchema ( IterInfo & info, XMP_VarString & schemaName ) +{ + + info.currSchema = schemaName; + #if 0 // *** XMP_DebugBuild + info._schemaPtr = info.currSchema.c_str(); + #endif + +} // SetCurrSchema + +// ------------------------------------------------------------------------------------------------- +// AdvanceIterPos +// -------------- +// +// Adjust currPos and possibly endPos for the next step in a pre-order depth-first traversal. The +// current node has just been visited, move on to its qualifiers, children, then siblings, or back +// up to an ancestor. AdvanceIterPos either moves to a property or qualifier node that can be +// visited, or to the end of the entire iteration. + +static void +AdvanceIterPos ( IterInfo & info ) +{ + // ------------------------------------------------------------------------------------------- + // Keep looking until we find a node to visit or the end of everything. The first time through + // the current node will exist, we just visited it. But we have to keep looking if the current + // node was the last of its siblings or is an empty schema. + + // ! It is possible that info.currPos == info.endPos on entry. Don't dereference info.currPos yet! + + while ( true ) { + + if ( info.currPos == info.endPos ) { + + // ------------------------------------------------------------------------------------ + // At the end of a set of siblings, move up to an ancestor. We've either just finished + // the qualifiers and will move to the children, or have just finished the children and + // will move on to the next sibling. + + if ( info.ancestors.empty() ) break; // We're at the end of the schema list. + + IterPosPair & parent = info.ancestors.back(); + info.currPos = parent.first; + info.endPos = parent.second; + info.ancestors.pop_back(); + + #if TraceIterators + printf ( " Moved up to %s, stage = %s\n", + info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] ); + #endif + + } else { + + // ------------------------------------------------------------------------------------------- + // Decide what to do with this iteration node based on its state. Don't use a switch statment, + // some of the cases want to break from the loop. A break in a switch just exits the case. + + #if TraceIterators + printf ( " Moving from %s, stage = %s\n", + info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] ); + #endif + + if ( info.currPos->visitStage == kIter_BeforeVisit ) { // Visit this node now. + if ( info.currPos->options & kXMP_SchemaNode ) SetCurrSchema ( info, info.currPos->fullPath ); + break; + } + + if ( info.currPos->visitStage == kIter_VisitSelf ) { // Just finished visiting the value portion. + info.currPos->visitStage = kIter_VisitQualifiers; // Start visiting the qualifiers. + if ( ! info.currPos->qualifiers.empty() ) { + info.ancestors.push_back ( IterPosPair ( info.currPos, info.endPos ) ); + info.endPos = info.currPos->qualifiers.end(); // ! Set the parent's endPos before changing currPos! + info.currPos = info.currPos->qualifiers.begin(); + break; + } + } + + if ( info.currPos->visitStage == kIter_VisitQualifiers ) { // Just finished visiting the qualifiers. + info.currPos->qualifiers.clear(); + info.currPos->visitStage = kIter_VisitChildren; // Start visiting the children. + if ( ! info.currPos->children.empty() ) { + info.ancestors.push_back ( IterPosPair ( info.currPos, info.endPos ) ); + info.endPos = info.currPos->children.end(); // ! Set the parent's endPos before changing currPos! + info.currPos = info.currPos->children.begin(); + break; + } + } + + if ( info.currPos->visitStage == kIter_VisitChildren ) { // Just finished visiting the children. + info.currPos->children.clear(); + ++info.currPos; // Move to the next sibling. + continue; + } + + #if TraceIterators + if ( info.currPos != info.endPos ) { + printf ( " Moved to %s, stage = %s\n", + info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] ); + } + #endif + + } + + } // Loop to find the next node. + + XMP_Assert ( (info.currPos == info.endPos) || (info.currPos->visitStage == kIter_BeforeVisit) ); + +} // AdvanceIterPos + +// ------------------------------------------------------------------------------------------------- +// GetNextXMPNode +// -------------- +// +// Used by XMPIterator::Next to obtain the next XMP node, ignoring the kXMP_IterJustLeafNodes flag. +// This isolates some messy code, allowing a clean loop in Next if kXMP_IterJustLeafNodes is set. + +static const XMP_Node * +GetNextXMPNode ( IterInfo & info ) +{ + const XMP_Node * xmpNode = 0; + + // ---------------------------------------------------------------------------------------------- + // On entry currPos points to an iteration node whose state is either before-visit or visit-self. + // If it is before-visit then we will return that node's value part now. If it is visit-self it + // means the previous iteration returned the value portion of that node, so we can advance to the + // next node in the iteration tree. Then we find the corresponding XMP node, allowing for the XMP + // tree to have been modified since that part of the iteration tree was constructed. + + // ! NOTE: Supporting aliases throws in some nastiness with schemas. There might not be any XMP + // ! node for the schema, but we still have to visit it because of possible aliases. The static + // ! sDummySchema is returned if there is no real schema node. + + if ( info.currPos->visitStage != kIter_BeforeVisit ) AdvanceIterPos ( info ); + + bool isSchemaNode = false; + XMP_ExpandedXPath expPath; // Keep outside the loop to avoid constant construct/destruct. + + while ( info.currPos != info.endPos ) { + + isSchemaNode = XMP_NodeIsSchema ( info.currPos->options ); + if ( isSchemaNode ) { + SetCurrSchema ( info, info.currPos->fullPath ); + xmpNode = FindConstSchema ( &info.xmpObj->tree, info.currPos->fullPath.c_str() ); + if ( xmpNode == 0 ) xmpNode = sDummySchema; + } else { + ExpandXPath ( info.currSchema.c_str(), info.currPos->fullPath.c_str(), &expPath ); + xmpNode = FindConstNode ( &info.xmpObj->tree, expPath ); + } + if ( xmpNode != 0 ) break; // Exit the loop, we found a live XMP node. + + info.currPos->visitStage = kIter_VisitChildren; // Make AdvanceIterPos move to the next sibling. + info.currPos->children.clear(); + info.currPos->qualifiers.clear(); + AdvanceIterPos ( info ); + + } + + if ( info.currPos == info.endPos ) return 0; + + // ------------------------------------------------------------------------------------------- + // Now we've got the iteration node and corresponding XMP node. Add the iteration children for + // structs and arrays. The children of schema were added when the iterator was constructed. + + XMP_Assert ( info.currPos->visitStage == kIter_BeforeVisit ); + + if ( info.currPos->visitStage == kIter_BeforeVisit ) { + if ( (! isSchemaNode) && (! (info.options & kXMP_IterJustChildren)) ) { + AddNodeOffspring ( info, *info.currPos, xmpNode ); + } + info.currPos->visitStage = kIter_VisitSelf; + } + + return xmpNode; + +} // GetNextXMPNode + +// ================================================================================================= +// Init/Term +// ================================================================================================= + +// ------------------------------------------------------------------------------------------------- +// Initialize +// ---------- + +/* class static */ bool +XMPIterator::Initialize() +{ + sDummySchema = new XMP_Node ( 0, "dummy:schema/", kXMP_SchemaNode); + return true; + +} // Initialize + +// ------------------------------------------------------------------------------------------------- +// Terminate +// ---------- + +/* class static */ void +XMPIterator::Terminate() RELEASE_NO_THROW +{ + delete ( sDummySchema ); + sDummySchema = 0; + return; + +} // Terminate + +// ================================================================================================= +// Constructors +// ================================================================================================= + +// ------------------------------------------------------------------------------------------------- +// XMPIterator +// ----------- +// +// Constructor for iterations over the nodes in an XMPMeta object. This builds a tree of iteration +// nodes that caches the existing node names of the XMPMeta object. The iteration tree is a partial +// replica of the XMPMeta tree. The initial iteration tree normally has just the root node, all of +// the schema nodes for a full object iteration. Lower level nodes (children and qualifiers) are +// added when the parent is visited. If the kXMP_IterJustChildren option is passed then the initial +// iterator includes the children and the parent is marked as done. The iteration tree nodes are +// pruned when they are no longer needed. + +XMPIterator::XMPIterator ( const XMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options ) : info(IterInfo(options,&xmpObj)), clientRefs(0) +{ + if ( (options & kXMP_IterClassMask) != kXMP_IterProperties ) { + XMP_Throw ( "Unsupported iteration kind", kXMPErr_BadOptions ); + } + + // *** Lock the XMPMeta object if we ever stop using a full DLL lock. + + if ( *propName != 0 ) { + + // An iterator rooted at a specific node. + + #if TraceIterators + printf ( "\nNew XMP property iterator for \"%s\", options = %X\n Schema = %s, root = %s\n", + xmpObj.tree.name.c_str(), options, schemaNS, propName ); + #endif + + XMP_ExpandedXPath propPath; + ExpandXPath ( schemaNS, propName, &propPath ); + XMP_Node * propNode = FindConstNode ( &xmpObj.tree, propPath ); // If not found get empty iteration. + + if ( propNode != 0 ) { + + XMP_VarString rootName ( propPath[1].step ); // The schema is [0]. + for ( size_t i = 2; i < propPath.size(); ++i ) { + XMP_OptionBits stepKind = GetStepKind ( propPath[i].options ); + if ( stepKind <= kXMP_QualifierStep ) rootName += '/'; + rootName += propPath[i].step; + } + + propName = rootName.c_str(); + size_t leafOffset = rootName.size(); + while ( (leafOffset > 0) && (propName[leafOffset] != '/') && (propName[leafOffset] != '[') ) --leafOffset; + if ( propName[leafOffset] == '/' ) ++leafOffset; + + info.tree.children.push_back ( IterNode ( propNode->options, propName, leafOffset ) ); + SetCurrSchema ( info, propPath[kSchemaStep].step.c_str() ); + if ( info.options & kXMP_IterJustChildren ) { + AddNodeOffspring ( info, info.tree.children.back(), propNode ); + } + + } + + } else if ( *schemaNS != 0 ) { + + // An iterator for all properties in one schema. + + #if TraceIterators + printf ( "\nNew XMP schema iterator for \"%s\", options = %X\n Schema = %s\n", + xmpObj.tree.name.c_str(), options, schemaNS ); + #endif + + info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, schemaNS, 0 ) ); + IterNode & iterSchema = info.tree.children.back(); + + XMP_Node * xmpSchema = FindConstSchema ( &xmpObj.tree, schemaNS ); + if ( xmpSchema != 0 ) AddSchemaProps ( info, iterSchema, xmpSchema ); + + if ( iterSchema.children.empty() ) { + info.tree.children.pop_back(); // No properties, remove the schema node. + } else { + SetCurrSchema ( info, schemaNS ); + } + + } else { + + // An iterator for all properties in all schema. First add schema that exist (have children), + // adding aliases from them if appropriate. Then add schema that have no actual properties + // but do have aliases to existing properties, if we're including aliases in the iteration. + + #if TraceIterators + printf ( "\nNew XMP tree iterator for \"%s\", options = %X\n", + xmpObj.tree.name.c_str(), options ); + #endif + + // First pick up the schema that exist. + + for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum != schemaLim; ++schemaNum ) { + + const XMP_Node * xmpSchema = xmpObj.tree.children[schemaNum]; + info.tree.children.push_back ( IterNode ( kXMP_SchemaNode, xmpSchema->name, 0 ) ); + IterNode & iterSchema = info.tree.children.back(); + + if ( ! (info.options & kXMP_IterJustChildren) ) { + AddSchemaProps ( info, iterSchema, xmpSchema ); + if ( iterSchema.children.empty() ) info.tree.children.pop_back(); // No properties, remove the schema node. + } + + } + + } + + // Set the current iteration position to the first node to be visited. + + info.currPos = info.tree.children.begin(); + info.endPos = info.tree.children.end(); + + if ( (info.options & kXMP_IterJustChildren) && (info.currPos != info.endPos) && (*schemaNS != 0) ) { + info.currPos->visitStage = kIter_VisitSelf; + } + + #if TraceIterators + if ( info.currPos == info.endPos ) { + printf ( " ** Empty iteration **\n" ); + } else { + printf ( " Initial node %s, stage = %s, iterator @ %.8X\n", + info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this ); + } + #endif + +} // XMPIterator for XMPMeta objects + +// ------------------------------------------------------------------------------------------------- +// XMPIterator +// ----------- +// +// Constructor for iterations over global tables such as registered namespaces or aliases. + +XMPIterator::XMPIterator ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options ) : info(IterInfo(options,0)), clientRefs(0) +{ + + XMP_Throw ( "Unimplemented XMPIterator constructor for global tables", kXMPErr_Unimplemented ); + void * p; p = &schemaNS; p = &propName; p = &options; // Avoid unused param warnings. + +} // XMPIterator for global tables + +// ------------------------------------------------------------------------------------------------- +// ~XMPIterator +// ----------- + +XMPIterator::~XMPIterator() RELEASE_NO_THROW +{ + XMP_Assert ( this->clientRefs <= 0 ); + // Let everything else default. + +} // ~XMPIterator + +// ================================================================================================= +// Iteration Methods +// ================================================================================================= + +// ------------------------------------------------------------------------------------------------- +// Next +// ---- +// +// Do a preorder traversal of the cached nodes. + +// *** Need to document the relationships between currPos, endPos, and visitStage. + +bool +XMPIterator::Next ( XMP_StringPtr * schemaNS, + XMP_StringLen * nsSize, + XMP_StringPtr * propPath, + XMP_StringLen * pathSize, + XMP_StringPtr * propValue, + XMP_StringLen * valueSize, + XMP_OptionBits * propOptions ) +{ + // *** Lock the XMPMeta object if we ever stop using a full DLL lock. + + // ! NOTE: Supporting aliases throws in some nastiness with schemas. There might not be any XMP + // ! node for the schema, but we still have to visit it because of possible aliases. + + if ( info.currPos == info.endPos ) return false; // Happens at the start of an empty iteration. + + #if TraceIterators + printf ( "Next iteration from %s, stage = %s, iterator @ %.8X\n", + info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this ); + #endif + + const XMP_Node * xmpNode = GetNextXMPNode ( info ); + if ( xmpNode == 0 ) return false; + bool isSchemaNode = XMP_NodeIsSchema ( info.currPos->options ); + + if ( info.options & kXMP_IterJustLeafNodes ) { + while ( isSchemaNode || (! xmpNode->children.empty()) ) { + info.currPos->visitStage = kIter_VisitQualifiers; // Skip to this node's children. + xmpNode = GetNextXMPNode ( info ); + if ( xmpNode == 0 ) return false; + isSchemaNode = XMP_NodeIsSchema ( info.currPos->options ); + } + } + + *schemaNS = info.currSchema.c_str(); + *nsSize = info.currSchema.size(); + + *propOptions = info.currPos->options; + + *propPath = ""; + *pathSize = 0; + *propValue = ""; + *valueSize = 0; + + if ( ! (*propOptions & kXMP_SchemaNode) ) { + + *propPath = info.currPos->fullPath.c_str(); + *pathSize = info.currPos->fullPath.size(); + + if ( info.options & kXMP_IterJustLeafName ) { + *propPath += info.currPos->leafOffset; + *pathSize -= info.currPos->leafOffset; + xmpNode->GetLocalURI ( schemaNS, nsSize ); // Use the leaf namespace, not the top namespace. + } + + if ( ! (*propOptions & kXMP_PropCompositeMask) ) { + *propValue = xmpNode->value.c_str(); + *valueSize = xmpNode->value.size(); + } + + } + + #if TraceIterators + printf ( " Next node %s, stage = %s\n", + info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] ); + #endif + + return true; + +} // Next + +// ------------------------------------------------------------------------------------------------- +// Skip +// ---- +// +// Skip some portion of the traversal related to the last visited node. We skip either that node's +// children, or those children and the previous node's siblings. The implementation might look a bit +// awkward because info.currNode always points to the next node to be visited. We might already have +// moved past the things to skip, e.g. if the previous node was simple and the last of its siblings. + +enum { + kXMP_ValidIterSkipOptions = kXMP_IterSkipSubtree | kXMP_IterSkipSiblings +}; + +void +XMPIterator::Skip ( XMP_OptionBits iterOptions ) +{ +// if ( (info.currPos == kIter_NullPos) ) XMP_Throw ( "No prior postion to skip from", kXMPErr_BadIterPosition ); + if ( iterOptions == 0 ) XMP_Throw ( "Must specify what to skip", kXMPErr_BadOptions ); + if ( (iterOptions & ~kXMP_ValidIterSkipOptions) != 0 ) XMP_Throw ( "Undefined options", kXMPErr_BadOptions ); + + #if TraceIterators + printf ( "Skipping from %s, stage = %s, iterator @ %.8X", + info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage], this ); + #endif + + if ( iterOptions & kXMP_IterSkipSubtree ) { + #if TraceIterators + printf ( ", mode = subtree\n" ); + #endif + info.currPos->visitStage = kIter_VisitChildren; + } else if ( iterOptions & kXMP_IterSkipSiblings ) { + #if TraceIterators + printf ( ", mode = siblings\n" ); + #endif + info.currPos = info.endPos; + AdvanceIterPos ( info ); + } + #if TraceIterators + printf ( " Skipped to %s, stage = %s\n", + info.currPos->fullPath.c_str(), sStageNames[info.currPos->visitStage] ); + #endif + + +} // Skip + +// ================================================================================================= diff --git a/source/lib/xmp_core/XMPIterator.hpp b/source/lib/xmp_core/XMPIterator.hpp new file mode 100644 index 0000000..30146ae --- /dev/null +++ b/source/lib/xmp_core/XMPIterator.hpp @@ -0,0 +1,144 @@ +#ifndef __XMPIterator_hpp__ +#define __XMPIterator_hpp__ + +// ================================================================================================= +// Copyright 2003 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" +#include "public/include/XMP_Const.h" +#include "XMPMeta.hpp" + +// ================================================================================================= + +struct IterNode; +typedef std::vector < IterNode > IterOffspring; +typedef IterOffspring::iterator IterPos; + +typedef std::pair < IterPos, IterPos > IterPosPair; +typedef std::vector < IterPosPair > IterPosStack; + +enum { // Values for the visitStage field, used to decide how to proceed past a node. + kIter_BeforeVisit = 0, // Have not visited this node at all. + kIter_VisitSelf = 1, // Have visited this node and returned its value/options portion. + kIter_VisitQualifiers = 2, // In the midst of visiting this node's qualifiers. + kIter_VisitChildren = 3 // In the midst of visiting this node's children. +}; + +struct IterNode { + + XMP_OptionBits options; + XMP_VarString fullPath; + size_t leafOffset; + IterOffspring children, qualifiers; + XMP_Uns8 visitStage; + #if 0 // *** XMP_DebugBuild + XMP_StringPtr _pathPtr, _leafPtr; // *** Not working, need operator=? + #endif + + IterNode() : options(0), leafOffset(0), visitStage(kIter_BeforeVisit) + { + #if 0 // *** XMP_DebugBuild + _pathPtr = _leafPtr = 0; + #endif + }; + + IterNode ( XMP_OptionBits _options, const XMP_VarString& _fullPath, size_t _leafOffset ) + : options(_options), fullPath(_fullPath), leafOffset(_leafOffset), visitStage(kIter_BeforeVisit) + { + #if 0 // *** XMP_DebugBuild + _pathPtr = fullPath.c_str(); + _leafPtr = _pathPtr + leafOffset; + #endif + }; + +}; + +struct IterInfo { + + XMP_OptionBits options; + const XMPMeta * xmpObj; + XMP_VarString currSchema; + IterPos currPos, endPos; + IterPosStack ancestors; + IterNode tree; + #if 0 // *** XMP_DebugBuild + XMP_StringPtr _schemaPtr; // *** Not working, need operator=? + #endif + + IterInfo() : options(0), xmpObj(0) + { + #if 0 // *** XMP_DebugBuild + _schemaPtr = 0; + #endif + }; + + IterInfo ( XMP_OptionBits _options, const XMPMeta * _xmpObj ) : options(_options), xmpObj(_xmpObj) + { + #if 0 // *** XMP_DebugBuild + _schemaPtr = 0; + #endif + }; + +}; + +// ================================================================================================= + +class XMPIterator { +public: + + static bool + Initialize(); // ! For internal use only! + + static void + Terminate() RELEASE_NO_THROW; // ! For internal use only! + + XMPIterator ( const XMPMeta & xmpObj, // Construct a property iterator. + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options ); + + XMPIterator ( XMP_StringPtr schemaNS, // Construct a table iterator. + XMP_StringPtr propName, + XMP_OptionBits options ); + + virtual ~XMPIterator() RELEASE_NO_THROW; + + bool + Next ( XMP_StringPtr * schemaNS, + XMP_StringLen * nsSize, + XMP_StringPtr * propPath, + XMP_StringLen * pathSize, + XMP_StringPtr * propValue, + XMP_StringLen * valueSize, + XMP_OptionBits * propOptions ); + + void + Skip ( XMP_OptionBits options ); + + // ! Expose so that wrappers and file static functions can see the data. + + XMP_Int32 clientRefs; // ! Must be signed to allow decrement from 0. + XMP_ReadWriteLock lock; + + IterInfo info; + +private: + + // ! These are hidden on purpose: + XMPIterator() : clientRefs(0) + { XMP_Throw ( "Call to hidden constructor", kXMPErr_InternalFailure ); }; + XMPIterator ( const XMPIterator & /* original */ ) : clientRefs(0) + { XMP_Throw ( "Call to hidden constructor", kXMPErr_InternalFailure ); }; + void operator= ( const XMPIterator & /* rhs */ ) + { XMP_Throw ( "Call to hidden operator=", kXMPErr_InternalFailure ); }; + +}; + +// ================================================================================================= + +#endif // __XMPIterator_hpp__ diff --git a/source/lib/xmp_core/XMPMeta-GetSet.cpp b/source/lib/xmp_core/XMPMeta-GetSet.cpp new file mode 100644 index 0000000..44f10a4 --- /dev/null +++ b/source/lib/xmp_core/XMPMeta-GetSet.cpp @@ -0,0 +1,1309 @@ +// ================================================================================================= +// Copyright 2003 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// +// Adobe patent application tracking #P435, entitled 'Unique markers to simplify embedding data of +// one format in a file with a different format', inventors: Sean Parent, Greg Gilley. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! This must be the first include! +#include "XMPCore_Impl.hpp" + +#include "XMPMeta.hpp" +#include "XMPIterator.hpp" +#include "XMPUtils.hpp" + +#include "public/include/XMP_Version.h" +#include "UnicodeInlines.incl_cpp" +#include "UnicodeConversions.hpp" +#include "ExpatAdapter.hpp" + +#if XMP_DebugBuild + #include +#endif + +using namespace std; + +#if XMP_WinBuild + #pragma warning ( disable : 4533 ) // initialization of '...' is skipped by 'goto ...' + #pragma warning ( disable : 4702 ) // unreachable code + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) +#endif + + +// *** Use the XMP_PropIsXyz (Schema, Simple, Struct, Array, ...) macros +// *** Add debug codegen checks, e.g. that typical masking operations really work +// *** Change all uses of strcmp and strncmp to XMP_LitMatch and XMP_LitNMatch + + +// ================================================================================================= +// Local Types and Constants +// ========================= + +typedef unsigned char XMP_CLTMatch; + +enum { // Values for XMP_CLTMatch. + kXMP_CLT_NoValues, + kXMP_CLT_SpecificMatch, + kXMP_CLT_SingleGeneric, + kXMP_CLT_MultipleGeneric, + kXMP_CLT_XDefault, + kXMP_CLT_FirstItem +}; + + +// ================================================================================================= +// Static Variables +// ================ + + +// ================================================================================================= +// Local Utilities +// =============== + + +// ------------------------------------------------------------------------------------------------- +// SetNodeValue +// ------------ + +static inline void +SetNodeValue ( XMP_Node * node, XMP_StringPtr value ) +{ + + #if XMP_DebugBuild // ! Hack to force an assert. + if ( (node->name == "xmp:TestAssertNotify") && XMP_LitMatch ( value, "DoIt!" ) ) { + XMP_Assert ( node->name != "xmp:TestAssertNotify" ); + } + #endif + + std::string newValue = value; // Need a local copy to tweak and not change node.value for errors. + + XMP_Uns8* chPtr = (XMP_Uns8*) newValue.c_str(); // Check for valid UTF-8, replace ASCII controls with a space. + while ( *chPtr != 0 ) { + + while ( (*chPtr != 0) && (*chPtr < 0x80) ) { + if ( *chPtr < 0x20 ) { + if ( (*chPtr != kTab) && (*chPtr != kLF) && (*chPtr != kCR) ) *chPtr = 0x20; + } else if (*chPtr == 0x7F ) { + *chPtr = 0x20; + } + ++chPtr; + } + + XMP_Assert ( (*chPtr == 0) || (*chPtr >= 0x80) ); + + if ( *chPtr != 0 ) { + XMP_Uns32 cp = GetCodePoint ( (const XMP_Uns8 **) &chPtr ); // Throws for bad UTF-8. + if ( (cp == 0xFFFE) || (cp == 0xFFFF) ) { + XMP_Throw ( "U+FFFE and U+FFFF are not allowed in XML", kXMPErr_BadXML ); + } + } + + } + + if ( XMP_PropIsQualifier(node->options) && (node->name == "xml:lang") ) NormalizeLangValue ( &newValue ); + + node->value.swap ( newValue ); + + #if 0 // *** XMP_DebugBuild + node->_valuePtr = node->value.c_str(); + #endif + +} // SetNodeValue + + +// ------------------------------------------------------------------------------------------------- +// SetNode +// ------- +// +// The internals for SetProperty and related calls, used after the node is found or created. + +static void +SetNode ( XMP_Node * node, XMP_StringPtr value, XMP_OptionBits options ) +{ + if ( options & kXMP_DeleteExisting ) { + XMP_ClearOption ( options, kXMP_DeleteExisting ); + node->options = options; + node->value.erase(); + node->RemoveChildren(); + node->RemoveQualifiers(); + } + + node->options |= options; // Keep options set by FindNode when creating a new node. + + if ( value != 0 ) { + + // This is setting the value of a leaf node. + if ( node->options & kXMP_PropCompositeMask ) XMP_Throw ( "Composite nodes can't have values", kXMPErr_BadXPath ); + XMP_Assert ( node->children.empty() ); + SetNodeValue ( node, value ); + + } else { + + // This is setting up an array or struct. + if ( ! node->value.empty() ) XMP_Throw ( "Composite nodes can't have values", kXMPErr_BadXPath ); + if ( node->options & kXMP_PropCompositeMask ) { // Can't change an array to a struct, or vice versa. + if ( (options & kXMP_PropCompositeMask) != (node->options & kXMP_PropCompositeMask) ) { + XMP_Throw ( "Requested and existing composite form mismatch", kXMPErr_BadXPath ); + } + } + node->RemoveChildren(); + + } + +} // SetNode + + +// ------------------------------------------------------------------------------------------------- +// DoSetArrayItem +// -------------- + +static void +DoSetArrayItem ( XMP_Node * arrayNode, + XMP_Index itemIndex, + XMP_StringPtr itemValue, + XMP_OptionBits options ) +{ + XMP_OptionBits itemLoc = options & kXMP_PropArrayLocationMask; + XMP_Index arraySize = arrayNode->children.size(); + + options &= ~kXMP_PropArrayLocationMask; + options = VerifySetOptions ( options, itemValue ); + + // Now locate or create the item node and set the value. Note the index parameter is one-based! + // The index can be in the range [0..size+1] or "last", normalize it and check the insert flags. + // The order of the normalization checks is important. If the array is empty we end up with an + // index and location to set item size+1. + + XMP_Node * itemNode = 0; + + if ( itemIndex == kXMP_ArrayLastItem ) itemIndex = arraySize; + if ( (itemIndex == 0) && (itemLoc == kXMP_InsertAfterItem) ) { + itemIndex = 1; + itemLoc = kXMP_InsertBeforeItem; + } + if ( (itemIndex == arraySize) && (itemLoc == kXMP_InsertAfterItem) ) { + itemIndex += 1; + itemLoc = 0; + } + if ( (itemIndex == arraySize+1) && (itemLoc == kXMP_InsertBeforeItem) ) itemLoc = 0; + + if ( itemIndex == arraySize+1 ) { + + if ( itemLoc != 0 ) XMP_Throw ( "Can't insert before or after implicit new item", kXMPErr_BadIndex ); + itemNode = new XMP_Node ( arrayNode, kXMP_ArrayItemName, 0 ); + arrayNode->children.push_back ( itemNode ); + + } else { + + if ( (itemIndex < 1) || (itemIndex > arraySize) ) XMP_Throw ( "Array index out of bounds", kXMPErr_BadIndex ); + --itemIndex; // ! Convert the index to a C zero-based value! + if ( itemLoc == 0 ) { + itemNode = arrayNode->children[itemIndex]; + } else { + XMP_NodePtrPos itemPos = arrayNode->children.begin() + itemIndex; + if ( itemLoc == kXMP_InsertAfterItem ) ++itemPos; + itemNode = new XMP_Node ( arrayNode, kXMP_ArrayItemName, 0 ); + itemPos = arrayNode->children.insert ( itemPos, itemNode ); + } + + } + + SetNode ( itemNode, itemValue, options ); + +} // DoSetArrayItem + + +// ------------------------------------------------------------------------------------------------- +// ChooseLocalizedText +// ------------------- +// +// 1. Look for an exact match with the specific language. +// 2. If a generic language is given, look for partial matches. +// 3. Look for an "x-default" item. +// 4. Choose the first item. + +static XMP_CLTMatch +ChooseLocalizedText ( const XMP_Node * arrayNode, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + const XMP_Node * * itemNode ) +{ + const XMP_Node * currItem = 0; + const size_t itemLim = arrayNode->children.size(); + size_t itemNum; + + // See if the array has the right form. Allow empty alt arrays, that is what parsing returns. + // *** Should check alt-text bit when that is reliably maintained. + + if ( ! ( XMP_ArrayIsAltText(arrayNode->options) || + (arrayNode->children.empty() && XMP_ArrayIsAlternate(arrayNode->options)) ) ) { + XMP_Throw ( "Localized text array is not alt-text", kXMPErr_BadXPath ); + } + if ( arrayNode->children.empty() ) { + *itemNode = 0; + return kXMP_CLT_NoValues; + } + + for ( itemNum = 0; itemNum < itemLim; ++itemNum ) { + currItem = arrayNode->children[itemNum]; + if ( currItem->options & kXMP_PropCompositeMask ) { + XMP_Throw ( "Alt-text array item is not simple", kXMPErr_BadXPath ); + } + if ( currItem->qualifiers.empty() || (currItem->qualifiers[0]->name != "xml:lang") ) { + XMP_Throw ( "Alt-text array item has no language qualifier", kXMPErr_BadXPath ); + } + } + + // Look for an exact match with the specific language. + for ( itemNum = 0; itemNum < itemLim; ++itemNum ) { + currItem = arrayNode->children[itemNum]; + if ( currItem->qualifiers[0]->value == specificLang ) { + *itemNode = currItem; + return kXMP_CLT_SpecificMatch; + } + } + + if ( *genericLang != 0 ) { + + // Look for the first partial match with the generic language. + const size_t genericLen = strlen ( genericLang ); + for ( itemNum = 0; itemNum < itemLim; ++itemNum ) { + currItem = arrayNode->children[itemNum]; + XMP_StringPtr currLang = currItem->qualifiers[0]->value.c_str(); + const size_t currLangSize = currItem->qualifiers[0]->value.size(); + if ( (currLangSize >= genericLen) && + XMP_LitNMatch ( currLang, genericLang, genericLen ) && + ((currLangSize == genericLen) || (currLang[genericLen] == '-')) ) { + *itemNode = currItem; + break; // ! Don't return, need to look for other matches. + } + } + + if ( itemNum < itemLim ) { + + // Look for a second partial match with the generic language. + for ( ++itemNum; itemNum < itemLim; ++itemNum ) { + currItem = arrayNode->children[itemNum]; + XMP_StringPtr currLang = currItem->qualifiers[0]->value.c_str(); + const size_t currLangSize = currItem->qualifiers[0]->value.size(); + if ( (currLangSize >= genericLen) && + XMP_LitNMatch ( currLang, genericLang, genericLen ) && + ((currLangSize == genericLen) || (currLang[genericLen] == '-')) ) { + return kXMP_CLT_MultipleGeneric; // ! Leave itemNode with the first partial match. + } + } + return kXMP_CLT_SingleGeneric; // No second partial match was found. + + } + + } + + // Look for an 'x-default' item. + for ( itemNum = 0; itemNum < itemLim; ++itemNum ) { + currItem = arrayNode->children[itemNum]; + if ( currItem->qualifiers[0]->value == "x-default" ) { + *itemNode = currItem; + return kXMP_CLT_XDefault; + } + } + + // Everything failed, choose the first item. + *itemNode = arrayNode->children[0]; + return kXMP_CLT_FirstItem; + +} // ChooseLocalizedText + + +// ------------------------------------------------------------------------------------------------- +// AppendLangItem +// -------------- + +static void +AppendLangItem ( XMP_Node * arrayNode, XMP_StringPtr itemLang, XMP_StringPtr itemValue ) +{ + XMP_Node * newItem = new XMP_Node ( arrayNode, kXMP_ArrayItemName, (kXMP_PropHasQualifiers | kXMP_PropHasLang) ); + XMP_Node * langQual = new XMP_Node ( newItem, "xml:lang", kXMP_PropIsQualifier ); + + try { // ! Use SetNodeValue, not constructors above, to get the character checks. + SetNodeValue ( newItem, itemValue ); + SetNodeValue ( langQual, itemLang ); + } catch (...) { + delete newItem; + delete langQual; + throw; + } + + newItem->qualifiers.push_back ( langQual ); + + if ( (arrayNode->children.empty()) || (langQual->value != "x-default") ) { + arrayNode->children.push_back ( newItem ); + } else { + arrayNode->children.insert ( arrayNode->children.begin(), newItem ); + } + +} // AppendLangItem + + +// ================================================================================================= +// Class Methods +// ============= +// +// +// ================================================================================================= + + +// ------------------------------------------------------------------------------------------------- +// GetProperty +// ----------- + +bool +XMPMeta::GetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr * propValue, + XMP_StringLen * valueSize, + XMP_OptionBits * options ) const +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + XMP_Assert ( (propValue != 0) && (valueSize != 0) && (options != 0) ); // Enforced by wrapper. + + XMP_ExpandedXPath expPath; + ExpandXPath ( schemaNS, propName, &expPath ); + + XMP_Node * propNode = FindConstNode ( &tree, expPath ); + if ( propNode == 0 ) return false; + + *propValue = propNode->value.c_str(); + *valueSize = propNode->value.size(); + *options = propNode->options; + + return true; + +} // GetProperty + + +// ------------------------------------------------------------------------------------------------- +// GetArrayItem +// ------------ + +bool +XMPMeta::GetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_StringPtr * itemValue, + XMP_StringLen * valueSize, + XMP_OptionBits * options ) const +{ + XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper. + XMP_Assert ( (itemValue != 0) && (options != 0) ); // Enforced by wrapper. + + // ! Special case check to make errors consistent if the array does not exist. The other array + // ! functions and existing array here (empty or not) already throw. + if ( (itemIndex <= 0) && (itemIndex != kXMP_ArrayLastItem) ) XMP_Throw ( "Array index must be larger than zero", kXMPErr_BadXPath ); + + XMP_VarString itemPath; + XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath ); + return GetProperty ( schemaNS, itemPath.c_str(), itemValue, valueSize, options ); + +} // GetArrayItem + + +// ------------------------------------------------------------------------------------------------- +// GetStructField +// -------------- + +bool +XMPMeta::GetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr * fieldValue, + XMP_StringLen * valueSize, + XMP_OptionBits * options ) const +{ + XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper. + XMP_Assert ( (fieldValue != 0) && (options != 0) ); // Enforced by wrapper. + + XMP_VarString fieldPath; + XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath ); + return GetProperty ( schemaNS, fieldPath.c_str(), fieldValue, valueSize, options ); + +} // GetStructField + + +// ------------------------------------------------------------------------------------------------- +// GetQualifier +// ------------ + +bool +XMPMeta::GetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_StringPtr * qualValue, + XMP_StringLen * valueSize, + XMP_OptionBits * options ) const +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper. + XMP_Assert ( (qualValue != 0) && (options != 0) ); // Enforced by wrapper. + + XMP_VarString qualPath; + XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath ); + return GetProperty ( schemaNS, qualPath.c_str(), qualValue, valueSize, options ); + +} // GetQualifier + + +// ------------------------------------------------------------------------------------------------- +// SetProperty +// ----------- + +// *** Should handle array items specially, calling SetArrayItem. + +void +XMPMeta::SetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr propValue, + XMP_OptionBits options ) +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + + options = VerifySetOptions ( options, propValue ); + + XMP_ExpandedXPath expPath; + ExpandXPath ( schemaNS, propName, &expPath ); + + XMP_Node * propNode = FindNode ( &tree, expPath, kXMP_CreateNodes, options ); + if ( propNode == 0 ) XMP_Throw ( "Specified property does not exist", kXMPErr_BadXPath ); + + SetNode ( propNode, propValue, options ); + +} // SetProperty + + +// ------------------------------------------------------------------------------------------------- +// SetArrayItem +// ------------ + +void +XMPMeta::SetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_StringPtr itemValue, + XMP_OptionBits options ) +{ + XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper. + + XMP_ExpandedXPath arrayPath; + ExpandXPath ( schemaNS, arrayName, &arrayPath ); + XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_ExistingOnly ); // Just lookup, don't try to create. + if ( arrayNode == 0 ) XMP_Throw ( "Specified array does not exist", kXMPErr_BadXPath ); + + DoSetArrayItem ( arrayNode, itemIndex, itemValue, options ); + +} // SetArrayItem + + +// ------------------------------------------------------------------------------------------------- +// AppendArrayItem +// --------------- + +void +XMPMeta::AppendArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + XMP_StringPtr itemValue, + XMP_OptionBits options ) +{ + XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper. + + arrayOptions = VerifySetOptions ( arrayOptions, 0 ); + if ( (arrayOptions & ~kXMP_PropArrayFormMask) != 0 ) { + XMP_Throw ( "Only array form flags allowed for arrayOptions", kXMPErr_BadOptions ); + } + + // Locate or create the array. If it already exists, make sure the array form from the options + // parameter is compatible with the current state. + + XMP_ExpandedXPath arrayPath; + ExpandXPath ( schemaNS, arrayName, &arrayPath ); + XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_ExistingOnly ); // Just lookup, don't try to create. + + if ( arrayNode != 0 ) { + // The array exists, make sure the form is compatible. Zero arrayForm means take what exists. + if ( ! (arrayNode->options & kXMP_PropValueIsArray) ) { + XMP_Throw ( "The named property is not an array", kXMPErr_BadXPath ); + } + #if 0 + // *** Disable for now. Need to do some general rethinking of semantic checks. + if ( (arrayOptions != 0) && (arrayOptions != (arrayNode->options & kXMP_PropArrayFormMask)) ) { + XMP_Throw ( "Mismatch of existing and specified array form", kXMPErr_BadOptions ); + } + #endif + } else { + // The array does not exist, try to create it. + if ( arrayOptions == 0 ) XMP_Throw ( "Explicit arrayOptions required to create new array", kXMPErr_BadOptions ); + arrayNode = FindNode ( &tree, arrayPath, kXMP_CreateNodes, arrayOptions ); + if ( arrayNode == 0 ) XMP_Throw ( "Failure creating array node", kXMPErr_BadXPath ); + } + + DoSetArrayItem ( arrayNode, kXMP_ArrayLastItem, itemValue, (options | kXMP_InsertAfterItem) ); + +} // AppendArrayItem + + +// ------------------------------------------------------------------------------------------------- +// SetStructField +// -------------- + +void +XMPMeta::SetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + XMP_OptionBits options ) +{ + XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper. + + XMP_VarString fieldPath; + XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath ); + SetProperty ( schemaNS, fieldPath.c_str(), fieldValue, options ); + +} // SetStructField + + +// ------------------------------------------------------------------------------------------------- +// SetQualifier +// ------------ + +void +XMPMeta::SetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_StringPtr qualValue, + XMP_OptionBits options ) +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper. + + XMP_ExpandedXPath expPath; + ExpandXPath ( schemaNS, propName, &expPath ); + XMP_Node * propNode = FindNode ( &tree, expPath, kXMP_ExistingOnly ); + if ( propNode == 0 ) XMP_Throw ( "Specified property does not exist", kXMPErr_BadXPath ); + + XMP_VarString qualPath; + XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath ); + SetProperty ( schemaNS, qualPath.c_str(), qualValue, options ); + +} // SetQualifier + + +// ------------------------------------------------------------------------------------------------- +// DeleteProperty +// -------------- + +void +XMPMeta::DeleteProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName ) +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + + XMP_ExpandedXPath expPath; + ExpandXPath ( schemaNS, propName, &expPath ); + + XMP_NodePtrPos ptrPos; + XMP_Node * propNode = FindNode ( &tree, expPath, kXMP_ExistingOnly, kXMP_NoOptions, &ptrPos ); + if ( propNode == 0 ) return; + XMP_Node * parentNode = propNode->parent; + + // Erase the pointer from the parent's vector, then delete the node and all below it. + + if ( ! (propNode->options & kXMP_PropIsQualifier) ) { + + parentNode->children.erase ( ptrPos ); + DeleteEmptySchema ( parentNode ); + + } else { + + if ( propNode->name == "xml:lang" ) { + XMP_Assert ( parentNode->options & kXMP_PropHasLang ); // *** &= ~flag would be safer + parentNode->options ^= kXMP_PropHasLang; + } else if ( propNode->name == "rdf:type" ) { + XMP_Assert ( parentNode->options & kXMP_PropHasType ); + parentNode->options ^= kXMP_PropHasType; + } + + parentNode->qualifiers.erase ( ptrPos ); + XMP_Assert ( parentNode->options & kXMP_PropHasQualifiers ); + if ( parentNode->qualifiers.empty() ) parentNode->options ^= kXMP_PropHasQualifiers; + + } + + delete propNode; // ! The destructor takes care of the whole subtree. + +} // DeleteProperty + + +// ------------------------------------------------------------------------------------------------- +// DeleteArrayItem +// --------------- + +void +XMPMeta::DeleteArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex ) +{ + XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper. + + XMP_VarString itemPath; + XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath ); + DeleteProperty ( schemaNS, itemPath.c_str() ); + +} // DeleteArrayItem + + +// ------------------------------------------------------------------------------------------------- +// DeleteStructField +// ----------------- + +void +XMPMeta::DeleteStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName ) +{ + XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper. + + XMP_VarString fieldPath; + XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath ); + DeleteProperty ( schemaNS, fieldPath.c_str() ); + +} // DeleteStructField + + +// ------------------------------------------------------------------------------------------------- +// DeleteQualifier +// --------------- + +void +XMPMeta::DeleteQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName ) +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper. + + XMP_VarString qualPath; + XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath ); + DeleteProperty ( schemaNS, qualPath.c_str() ); + +} // DeleteQualifier + + +// ------------------------------------------------------------------------------------------------- +// DoesPropertyExist +// ----------------- + +bool +XMPMeta::DoesPropertyExist ( XMP_StringPtr schemaNS, + XMP_StringPtr propName ) const +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + + XMP_ExpandedXPath expPath; + ExpandXPath ( schemaNS, propName, &expPath ); + + XMP_Node * propNode = FindConstNode ( &tree, expPath ); + return (propNode != 0); + +} // DoesPropertyExist + + +// ------------------------------------------------------------------------------------------------- +// DoesArrayItemExist +// ------------------ + +bool +XMPMeta::DoesArrayItemExist ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex ) const +{ + XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper. + + XMP_VarString itemPath; + XMPUtils::ComposeArrayItemPath ( schemaNS, arrayName, itemIndex, &itemPath ); + return DoesPropertyExist ( schemaNS, itemPath.c_str() ); + +} // DoesArrayItemExist + + +// ------------------------------------------------------------------------------------------------- +// DoesStructFieldExist +// -------------------- + +bool +XMPMeta::DoesStructFieldExist ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName ) const +{ + XMP_Assert ( (schemaNS != 0) && (structName != 0) && (fieldNS != 0) && (fieldName != 0) ); // Enforced by wrapper. + + XMP_VarString fieldPath; + XMPUtils::ComposeStructFieldPath ( schemaNS, structName, fieldNS, fieldName, &fieldPath ); + return DoesPropertyExist ( schemaNS, fieldPath.c_str() ); + +} // DoesStructFieldExist + + +// ------------------------------------------------------------------------------------------------- +// DoesQualifierExist +// ------------------ + +bool +XMPMeta::DoesQualifierExist ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName ) const +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) && (qualNS != 0) && (qualName != 0) ); // Enforced by wrapper. + + XMP_VarString qualPath; + XMPUtils::ComposeQualifierPath ( schemaNS, propName, qualNS, qualName, &qualPath ); + return DoesPropertyExist ( schemaNS, qualPath.c_str() ); + +} // DoesQualifierExist + + +// ------------------------------------------------------------------------------------------------- +// GetLocalizedText +// ---------------- + +bool +XMPMeta::GetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr _genericLang, + XMP_StringPtr _specificLang, + XMP_StringPtr * actualLang, + XMP_StringLen * langSize, + XMP_StringPtr * itemValue, + XMP_StringLen * valueSize, + XMP_OptionBits * options ) const +{ + XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) ); // Enforced by wrapper. + XMP_Assert ( (actualLang != 0) && (langSize != 0) ); // Enforced by wrapper. + XMP_Assert ( (itemValue != 0) && (valueSize != 0) && (options != 0) ); // Enforced by wrapper. + + XMP_VarString zGenericLang ( _genericLang ); + XMP_VarString zSpecificLang ( _specificLang ); + NormalizeLangValue ( &zGenericLang ); + NormalizeLangValue ( &zSpecificLang ); + + XMP_StringPtr genericLang = zGenericLang.c_str(); + XMP_StringPtr specificLang = zSpecificLang.c_str(); + + XMP_ExpandedXPath arrayPath; + ExpandXPath ( schemaNS, arrayName, &arrayPath ); + + const XMP_Node * arrayNode = FindConstNode ( &tree, arrayPath ); // *** This expand/find idiom is used in 3 Getters. + if ( arrayNode == 0 ) return false; // *** Should extract it into a local utility. + + XMP_CLTMatch match; + const XMP_Node * itemNode; + + match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, &itemNode ); + if ( match == kXMP_CLT_NoValues ) return false; + + *actualLang = itemNode->qualifiers[0]->value.c_str(); + *langSize = itemNode->qualifiers[0]->value.size(); + *itemValue = itemNode->value.c_str(); + *valueSize = itemNode->value.size(); + *options = itemNode->options; + + return true; + +} // GetLocalizedText + + +// ------------------------------------------------------------------------------------------------- +// SetLocalizedText +// ---------------- + +void +XMPMeta::SetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr _genericLang, + XMP_StringPtr _specificLang, + XMP_StringPtr itemValue, + XMP_OptionBits options ) +{ + IgnoreParam(options); + + XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) ); // Enforced by wrapper. + + XMP_VarString zGenericLang ( _genericLang ); + XMP_VarString zSpecificLang ( _specificLang ); + NormalizeLangValue ( &zGenericLang ); + NormalizeLangValue ( &zSpecificLang ); + + XMP_StringPtr genericLang = zGenericLang.c_str(); + XMP_StringPtr specificLang = zSpecificLang.c_str(); + + XMP_ExpandedXPath arrayPath; + ExpandXPath ( schemaNS, arrayName, &arrayPath ); + + // Find the array node and set the options if it was just created. + XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_CreateNodes, + (kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate) ); + if ( arrayNode == 0 ) XMP_Throw ( "Failed to find or create array node", kXMPErr_BadXPath ); + if ( ! XMP_ArrayIsAltText(arrayNode->options) ) { + if ( arrayNode->children.empty() && XMP_ArrayIsAlternate(arrayNode->options) ) { + arrayNode->options |= kXMP_PropArrayIsAltText; + } else { + XMP_Throw ( "Localized text array is not alt-text", kXMPErr_BadXPath ); + } + } + + // Make sure the x-default item, if any, is first. + + size_t itemNum, itemLim; + XMP_Node * xdItem = 0; + bool haveXDefault = false; + + for ( itemNum = 0, itemLim = arrayNode->children.size(); itemNum < itemLim; ++itemNum ) { + XMP_Node * currItem = arrayNode->children[itemNum]; + XMP_Assert ( XMP_PropHasLang(currItem->options) ); + if ( currItem->qualifiers.empty() || (currItem->qualifiers[0]->name != "xml:lang") ) { + XMP_Throw ( "Language qualifier must be first", kXMPErr_BadXPath ); + } + if ( currItem->qualifiers[0]->value == "x-default" ) { + xdItem = currItem; + haveXDefault = true; + break; + } + } + + if ( haveXDefault && (itemNum != 0) ) { + XMP_Assert ( arrayNode->children[itemNum]->qualifiers[0]->value == "x-default" ); + XMP_Node * temp = arrayNode->children[0]; + arrayNode->children[0] = arrayNode->children[itemNum]; + arrayNode->children[itemNum] = temp; + } + + // Find the appropriate item. ChooseLocalizedText will make sure the array is a language alternative. + + const XMP_Node * cItemNode; // ! ChooseLocalizedText returns a pointer to a const node. + XMP_CLTMatch match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, &cItemNode ); + XMP_Node * itemNode = const_cast ( cItemNode ); + + const bool specificXDefault = XMP_LitMatch ( specificLang, "x-default" ); + + switch ( match ) { + + case kXMP_CLT_NoValues : + + // Create the array items for the specificLang and x-default, with x-default first. + AppendLangItem ( arrayNode, "x-default", itemValue ); + haveXDefault = true; + if ( ! specificXDefault ) AppendLangItem ( arrayNode, specificLang, itemValue ); + break; + + case kXMP_CLT_SpecificMatch : + + if ( ! specificXDefault ) { + // Update the specific item, update x-default if it matches the old value. + if ( xdItem != NULL && haveXDefault && (xdItem != itemNode) && (xdItem->value == itemNode->value) ) { + SetNodeValue ( xdItem, itemValue ); + } + SetNodeValue ( itemNode, itemValue ); // ! Do this after the x-default check! + } else { + // Update all items whose values match the old x-default value. + XMP_Assert ( xdItem != NULL && haveXDefault && (xdItem == itemNode) ); + for ( itemNum = 0, itemLim = arrayNode->children.size(); itemNum < itemLim; ++itemNum ) { + XMP_Node * currItem = arrayNode->children[itemNum]; + if ( (currItem == xdItem) || (currItem->value != xdItem->value) ) continue; + SetNodeValue ( currItem, itemValue ); + } + SetNodeValue ( xdItem, itemValue ); // And finally do the x-default item. + } + break; + + case kXMP_CLT_SingleGeneric : + + // Update the generic item, update x-default if it matches the old value. + if ( xdItem != NULL && haveXDefault && (xdItem != itemNode) && (xdItem->value == itemNode->value) ) { + SetNodeValue ( xdItem, itemValue ); + } + SetNodeValue ( itemNode, itemValue ); // ! Do this after the x-default check! + break; + + case kXMP_CLT_MultipleGeneric : + + // Create the specific language, ignore x-default. + AppendLangItem ( arrayNode, specificLang, itemValue ); + if ( specificXDefault ) haveXDefault = true; + break; + + case kXMP_CLT_XDefault : + + // Create the specific language, update x-default if it was the only item. + if ( arrayNode->children.size() == 1 ) SetNodeValue ( xdItem, itemValue ); + AppendLangItem ( arrayNode, specificLang, itemValue ); + break; + + case kXMP_CLT_FirstItem : + + // Create the specific language, don't add an x-default item. + AppendLangItem ( arrayNode, specificLang, itemValue ); + if ( specificXDefault ) haveXDefault = true; + break; + + default : + XMP_Throw ( "Unexpected result from ChooseLocalizedText", kXMPErr_InternalFailure ); + + } + + // Add an x-default at the front if needed. + if ( (! haveXDefault) && (arrayNode->children.size() == 1) ) { + AppendLangItem ( arrayNode, "x-default", itemValue ); + } + +} // SetLocalizedText + +// ------------------------------------------------------------------------------------------------- +// DeleteLocalizedText +// ------------------- + +void +XMPMeta::DeleteLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr _genericLang, + XMP_StringPtr _specificLang ) +{ + XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (_genericLang != 0) && (_specificLang != 0) ); // Enforced by wrapper. + + XMP_VarString zGenericLang ( _genericLang ); + XMP_VarString zSpecificLang ( _specificLang ); + NormalizeLangValue ( &zGenericLang ); + NormalizeLangValue ( &zSpecificLang ); + + XMP_StringPtr genericLang = zGenericLang.c_str(); + XMP_StringPtr specificLang = zSpecificLang.c_str(); + + XMP_ExpandedXPath arrayPath; + ExpandXPath ( schemaNS, arrayName, &arrayPath ); + + // Find the LangAlt array and the selected array item. + + XMP_Node * arrayNode = FindNode ( &tree, arrayPath, kXMP_ExistingOnly ); + if ( arrayNode == 0 ) return; + size_t arraySize = arrayNode->children.size(); + + XMP_CLTMatch match; + XMP_Node * itemNode; + + match = ChooseLocalizedText ( arrayNode, genericLang, specificLang, (const XMP_Node **) &itemNode ); + if ( match != kXMP_CLT_SpecificMatch ) return; + + size_t itemIndex = 0; + for ( ; itemIndex < arraySize; ++itemIndex ) { + if ( arrayNode->children[itemIndex] == itemNode ) break; + } + XMP_Enforce ( itemIndex < arraySize ); + + // Decide if the selected item is x-default or not, find relevant matching item. + + bool itemIsXDefault = false; + if ( ! itemNode->qualifiers.empty() ) { + XMP_Node * qualNode = itemNode->qualifiers[0]; + if ( (qualNode->name == "xml:lang") && (qualNode->value == "x-default") ) itemIsXDefault = true; + } + + if ( itemIsXDefault && (itemIndex != 0) ) { // Enforce the x-default is first policy. + XMP_Node * temp = arrayNode->children[0]; + arrayNode->children[0] = arrayNode->children[itemIndex]; + arrayNode->children[itemIndex] = temp; + itemIndex = 0; + } + + XMP_Node * assocNode = 0; + size_t assocIndex; + size_t assocIsXDefault = false; + + if ( itemIsXDefault ) { + + for ( assocIndex = 1; assocIndex < arraySize; ++assocIndex ) { + if ( arrayNode->children[assocIndex]->value == itemNode->value ) { + assocNode = arrayNode->children[assocIndex]; + break; + } + } + + } else if ( itemIndex > 0 ) { + + XMP_Node * itemZero = arrayNode->children[0]; + if ( itemZero->value == itemNode->value ) { + XMP_Node * qualNode = itemZero->qualifiers[0]; + if ( (qualNode->name == "xml:lang") && (qualNode->value == "x-default") ) { + assocNode = arrayNode->children[0]; + assocIndex = 0; + assocIsXDefault = true; + } + } + + } + + // Delete the appropriate nodes. + + XMP_NodePtrPos arrayBegin = arrayNode->children.begin(); + + if ( assocNode == 0 ) { + arrayNode->children.erase ( arrayBegin + itemIndex ); + } else if ( itemIndex < assocIndex ) { + arrayNode->children.erase ( arrayBegin + assocIndex ); + arrayNode->children.erase ( arrayBegin + itemIndex ); + } else { + arrayNode->children.erase ( arrayBegin + itemIndex ); + arrayNode->children.erase ( arrayBegin + assocIndex ); + } + + delete itemNode; + if ( assocNode != 0 ) delete assocNode; + +} // DeleteLocalizedText + +// ------------------------------------------------------------------------------------------------- +// GetProperty_Bool +// ---------------- + +bool +XMPMeta::GetProperty_Bool ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool * propValue, + XMP_OptionBits * options ) const +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper. + + XMP_StringPtr valueStr; + XMP_StringLen valueLen; + + bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options ); + if ( found ) { + if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath ); + *propValue = XMPUtils::ConvertToBool ( valueStr ); + } + return found; + +} // GetProperty_Bool + + +// ------------------------------------------------------------------------------------------------- +// GetProperty_Int +// --------------- + +bool +XMPMeta::GetProperty_Int ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 * propValue, + XMP_OptionBits * options ) const +{ + XMP_Int64 tempValue64 = 0; + if ( GetProperty_Int64( schemaNS, propName, &tempValue64, options ) ) { + if ( tempValue64 < (XMP_Int64) Min_XMP_Int32 || tempValue64 > (XMP_Int64) Max_XMP_Int32 ) { + // overflow condition + XMP_Throw ( "Overflow condition", kXMPErr_BadValue ); + } else { + *propValue = (XMP_Int32) tempValue64; + return true; + } + } + return false; + +} // GetProperty_Int + + +// ------------------------------------------------------------------------------------------------- +// GetProperty_Int64 +// ----------------- + +bool +XMPMeta::GetProperty_Int64 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 * propValue, + XMP_OptionBits * options ) const +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper. + + XMP_StringPtr valueStr; + XMP_StringLen valueLen; + + bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options ); + if ( found ) { + if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath ); + std::string propValueStr; + propValueStr.append( valueStr, valueLen ); + XMPUtils::Trim( propValueStr ); + *propValue = XMPUtils::ConvertToInt64 ( propValueStr.c_str() ); + } + return found; + +} // GetProperty_Int64 + + +// ------------------------------------------------------------------------------------------------- +// GetProperty_Float +// ----------------- + +bool +XMPMeta::GetProperty_Float ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double * propValue, + XMP_OptionBits * options ) const +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper. + + XMP_StringPtr valueStr; + XMP_StringLen valueLen; + + bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options ); + if ( found ) { + if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath ); + std::string propValueStr; + propValueStr.append( valueStr, valueLen ); + XMPUtils::Trim( propValueStr ); + *propValue = XMPUtils::ConvertToFloat ( propValueStr.c_str() ); + } + return found; + +} // GetProperty_Float + + +// ------------------------------------------------------------------------------------------------- +// GetProperty_Date +// ---------------- + +bool +XMPMeta::GetProperty_Date ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_DateTime * propValue, + XMP_OptionBits * options ) const +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + XMP_Assert ( (propValue != 0) && (options != 0) ); // Enforced by wrapper. + + XMP_StringPtr valueStr; + XMP_StringLen valueLen; + + bool found = GetProperty ( schemaNS, propName, &valueStr, &valueLen, options ); + if ( found ) { + if ( ! XMP_PropIsSimple ( *options ) ) XMP_Throw ( "Property must be simple", kXMPErr_BadXPath ); + XMPUtils::ConvertToDate ( valueStr, propValue ); + } + return found; + +} // GetProperty_Date + + +// ------------------------------------------------------------------------------------------------- +// SetProperty_Bool +// ---------------- + +void +XMPMeta::SetProperty_Bool ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool propValue, + XMP_OptionBits options ) +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + + XMP_VarString valueStr; + XMPUtils::ConvertFromBool ( propValue, &valueStr ); + SetProperty ( schemaNS, propName, valueStr.c_str(), options ); + +} // SetProperty_Bool + + +// ------------------------------------------------------------------------------------------------- +// SetProperty_Int +// --------------- + +void +XMPMeta::SetProperty_Int ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 propValue, + XMP_OptionBits options ) +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + + XMP_VarString valueStr; + XMPUtils::ConvertFromInt ( propValue, "", &valueStr ); + SetProperty ( schemaNS, propName, valueStr.c_str(), options ); + +} // SetProperty_Int + + +// ------------------------------------------------------------------------------------------------- +// SetProperty_Int64 +// ----------------- + +void +XMPMeta::SetProperty_Int64 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 propValue, + XMP_OptionBits options ) +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + + XMP_VarString valueStr; + XMPUtils::ConvertFromInt64 ( propValue, "", &valueStr ); + SetProperty ( schemaNS, propName, valueStr.c_str(), options ); + +} // SetProperty_Int64 + + +// ------------------------------------------------------------------------------------------------- +// SetProperty_Float +// ----------------- + +void +XMPMeta::SetProperty_Float ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double propValue, + XMP_OptionBits options ) +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + + XMP_VarString valueStr; + XMPUtils::ConvertFromFloat ( propValue, "", &valueStr ); + SetProperty ( schemaNS, propName, valueStr.c_str(), options ); + +} // SetProperty_Float + + +// ------------------------------------------------------------------------------------------------- +// SetProperty_Date +// ---------------- + +void +XMPMeta::SetProperty_Date ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const XMP_DateTime & propValue, + XMP_OptionBits options ) +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // Enforced by wrapper. + + XMP_VarString valueStr; + XMPUtils::ConvertFromDate ( propValue, &valueStr ); + SetProperty ( schemaNS, propName, valueStr.c_str(), options ); + +} // SetProperty_Date + +// ================================================================================================= + diff --git a/source/lib/xmp_core/XMPMeta-Parse.cpp b/source/lib/xmp_core/XMPMeta-Parse.cpp new file mode 100644 index 0000000..28f628a --- /dev/null +++ b/source/lib/xmp_core/XMPMeta-Parse.cpp @@ -0,0 +1,1277 @@ +// ================================================================================================= +// Copyright 2003 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// +// Adobe patent application tracking #P435, entitled 'Unique markers to simplify embedding data of +// one format in a file with a different format', inventors: Sean Parent, Greg Gilley. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! This must be the first include! +#include "XMPCore_Impl.hpp" + +#include "XMPMeta.hpp" +#include "XMPUtils.hpp" + +#include "UnicodeInlines.incl_cpp" +#include "UnicodeConversions.hpp" +#include "ExpatAdapter.hpp" + +#if XMP_DebugBuild + #include +#endif + +using namespace std; + +#if XMP_WinBuild + #pragma warning ( disable : 4533 ) // initialization of '...' is skipped by 'goto ...' + #pragma warning ( disable : 4702 ) // unreachable code + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) + #pragma warning ( disable : 4996 ) // '...' was declared deprecated +#endif + + +// *** Use the XMP_PropIsXyz (Schema, Simple, Struct, Array, ...) macros +// *** Add debug codegen checks, e.g. that typical masking operations really work +// *** Change all uses of strcmp and strncmp to XMP_LitMatch and XMP_LitNMatch + + +// ================================================================================================= +// Local Types and Constants +// ========================= + + +// ================================================================================================= +// Static Variables +// ================ + +#ifndef Trace_ParsingHackery + #define Trace_ParsingHackery 0 +#endif + +static const char * kReplaceLatin1[128] = + { + + // The 0x80..0x9F range is undefined in Latin-1, but is defined in Windows code page 1252. + // The bytes 0x81, 0x8D, 0x8F, 0x90, and 0x9D are formally undefined by Windows 1252, but + // their conversion API maps them to U+0081, etc. These are in XML's RestrictedChar set, so + // we map them to a space. + + "\xE2\x82\xAC", " ", "\xE2\x80\x9A", "\xC6\x92", // 0x80 .. 0x83 + "\xE2\x80\x9E", "\xE2\x80\xA6", "\xE2\x80\xA0", "\xE2\x80\xA1", // 0x84 .. 0x87 + "\xCB\x86", "\xE2\x80\xB0", "\xC5\xA0", "\xE2\x80\xB9", // 0x88 .. 0x8B + "\xC5\x92", " ", "\xC5\xBD", " ", // 0x8C .. 0x8F + + " ", "\xE2\x80\x98", "\xE2\x80\x99", "\xE2\x80\x9C", // 0x90 .. 0x93 + "\xE2\x80\x9D", "\xE2\x80\xA2", "\xE2\x80\x93", "\xE2\x80\x94", // 0x94 .. 0x97 + "\xCB\x9C", "\xE2\x84\xA2", "\xC5\xA1", "\xE2\x80\xBA", // 0x98 .. 0x9B + "\xC5\x93", " ", "\xC5\xBE", "\xC5\xB8", // 0x9C .. 0x9F + + // These are the UTF-8 forms of the official Latin-1 characters in the range 0xA0..0xFF. Not + // too surprisingly these map to U+00A0, etc. Which is the Unicode Latin Supplement range. + + "\xC2\xA0", "\xC2\xA1", "\xC2\xA2", "\xC2\xA3", "\xC2\xA4", "\xC2\xA5", "\xC2\xA6", "\xC2\xA7", // 0xA0 .. 0xA7 + "\xC2\xA8", "\xC2\xA9", "\xC2\xAA", "\xC2\xAB", "\xC2\xAC", "\xC2\xAD", "\xC2\xAE", "\xC2\xAF", // 0xA8 .. 0xAF + + "\xC2\xB0", "\xC2\xB1", "\xC2\xB2", "\xC2\xB3", "\xC2\xB4", "\xC2\xB5", "\xC2\xB6", "\xC2\xB7", // 0xB0 .. 0xB7 + "\xC2\xB8", "\xC2\xB9", "\xC2\xBA", "\xC2\xBB", "\xC2\xBC", "\xC2\xBD", "\xC2\xBE", "\xC2\xBF", // 0xB8 .. 0xBF + + "\xC3\x80", "\xC3\x81", "\xC3\x82", "\xC3\x83", "\xC3\x84", "\xC3\x85", "\xC3\x86", "\xC3\x87", // 0xC0 .. 0xC7 + "\xC3\x88", "\xC3\x89", "\xC3\x8A", "\xC3\x8B", "\xC3\x8C", "\xC3\x8D", "\xC3\x8E", "\xC3\x8F", // 0xC8 .. 0xCF + + "\xC3\x90", "\xC3\x91", "\xC3\x92", "\xC3\x93", "\xC3\x94", "\xC3\x95", "\xC3\x96", "\xC3\x97", // 0xD0 .. 0xD7 + "\xC3\x98", "\xC3\x99", "\xC3\x9A", "\xC3\x9B", "\xC3\x9C", "\xC3\x9D", "\xC3\x9E", "\xC3\x9F", // 0xD8 .. 0xDF + + "\xC3\xA0", "\xC3\xA1", "\xC3\xA2", "\xC3\xA3", "\xC3\xA4", "\xC3\xA5", "\xC3\xA6", "\xC3\xA7", // 0xE0 .. 0xE7 + "\xC3\xA8", "\xC3\xA9", "\xC3\xAA", "\xC3\xAB", "\xC3\xAC", "\xC3\xAD", "\xC3\xAE", "\xC3\xAF", // 0xE8 .. 0xEF + + "\xC3\xB0", "\xC3\xB1", "\xC3\xB2", "\xC3\xB3", "\xC3\xB4", "\xC3\xB5", "\xC3\xB6", "\xC3\xB7", // 0xF0 .. 0xF7 + "\xC3\xB8", "\xC3\xB9", "\xC3\xBA", "\xC3\xBB", "\xC3\xBC", "\xC3\xBD", "\xC3\xBE", "\xC3\xBF", // 0xF8 .. 0xFF + + }; + + +// ================================================================================================= +// Local Utilities +// =============== + + +#define IsHexDigit(ch) ( (('0' <= (ch)) && ((ch) <= '9')) || (('A' <= (ch)) && ((ch) <= 'F')) ) +#define HexDigitValue(ch) ( (((ch) - '0') < 10) ? ((ch) - '0') : ((ch) - 'A' + 10) ) + + +// ------------------------------------------------------------------------------------------------- +// PickBestRoot +// ------------ +// +// Pick the first x:xmpmeta among multiple root candidates. If there aren't any, pick the first bare +// rdf:RDF if that is allowed. The returned root is the rdf:RDF child if an x:xmpmeta element was +// chosen. The search is breadth first, so a higher level candiate is chosen over a lower level one +// that was textually earlier in the serialized XML. + +static const XML_Node * PickBestRoot ( const XML_Node & xmlParent, XMP_OptionBits options ) +{ + + // Look among this parent's content for x:xmpmeta. The recursion for x:xmpmeta is broader than + // the strictly defined choice, but gives us smaller code. + for ( size_t childNum = 0, childLim = xmlParent.content.size(); childNum < childLim; ++childNum ) { + const XML_Node * childNode = xmlParent.content[childNum]; + if ( childNode->kind != kElemNode ) continue; + if ( (childNode->name == "x:xmpmeta") || (childNode->name == "x:xapmeta") ) return PickBestRoot ( *childNode, 0 ); + } + // Look among this parent's content for a bare rdf:RDF if that is allowed. + if ( ! (options & kXMP_RequireXMPMeta) ) { + for ( size_t childNum = 0, childLim = xmlParent.content.size(); childNum < childLim; ++childNum ) { + const XML_Node * childNode = xmlParent.content[childNum]; + if ( childNode->kind != kElemNode ) continue; + if ( childNode->name == "rdf:RDF" ) return childNode; + } + } + + // Recurse into the content. + for ( size_t childNum = 0, childLim = xmlParent.content.size(); childNum < childLim; ++childNum ) { + const XML_Node * foundRoot = PickBestRoot ( *xmlParent.content[childNum], options ); + if ( foundRoot != 0 ) return foundRoot; + } + + return 0; + +} // PickBestRoot + +// ------------------------------------------------------------------------------------------------- +// FindRootNode +// ------------ +// +// Find the XML node that is the root of the XMP data tree. Generally this will be an outer node, +// but it could be anywhere if a general XML document is parsed (e.g. SVG). The XML parser counted +// all possible root nodes, and kept a pointer to the last one. If there is more than one possible +// root use PickBestRoot to choose among them. +// +// If there is a root node, try to extract the version of the previous XMP toolkit. + +static const XML_Node * FindRootNode ( const XMLParserAdapter & xmlParser, XMP_OptionBits options ) +{ + const XML_Node * rootNode = xmlParser.rootNode; + + if ( xmlParser.rootCount > 1 ) rootNode = PickBestRoot ( xmlParser.tree, options ); + if ( rootNode == 0 ) return 0; + + XMP_Assert ( rootNode->name == "rdf:RDF" ); + + if ( (options & kXMP_RequireXMPMeta) && + ((rootNode->parent == 0) || + ((rootNode->parent->name != "x:xmpmeta") && (rootNode->parent->name != "x:xapmeta"))) ) return 0; + + return rootNode; + +} // FindRootNode + +// ------------------------------------------------------------------------------------------------- +// NormalizeDCArrays +// ----------------- +// +// Undo the denormalization performed by the XMP used in Acrobat 5. If a Dublin Core array had only +// one item, it was serialized as a simple property. The xml:lang attribute was dropped from an +// alt-text item if the language was x-default. + +// *** This depends on the dc: namespace prefix. + +static void +NormalizeDCArrays ( XMP_Node * xmpTree ) +{ + XMP_Node * dcSchema = FindSchemaNode ( xmpTree, kXMP_NS_DC, kXMP_ExistingOnly ); + if ( dcSchema == 0 ) return; + + for ( size_t propNum = 0, propLimit = dcSchema->children.size(); propNum < propLimit; ++propNum ) { + XMP_Node * currProp = dcSchema->children[propNum]; + XMP_OptionBits arrayForm = 0; + + if ( ! XMP_PropIsSimple ( currProp->options ) ) continue; // Nothing to do if not simple. + + if ( (currProp->name == "dc:creator" ) || // See if it is supposed to be an array. + (currProp->name == "dc:date" ) ) { // *** Think about an array of char* and a loop. + arrayForm = kXMP_PropArrayIsOrdered; + } else if ( + (currProp->name == "dc:description" ) || + (currProp->name == "dc:rights" ) || + (currProp->name == "dc:title" ) ) { + arrayForm = kXMP_PropArrayIsAltText; + } else if ( + (currProp->name == "dc:contributor" ) || + (currProp->name == "dc:language" ) || + (currProp->name == "dc:publisher" ) || + (currProp->name == "dc:relation" ) || + (currProp->name == "dc:subject" ) || + (currProp->name == "dc:type" ) ) { + arrayForm = kXMP_PropValueIsArray; + } + if ( arrayForm == 0 ) continue; // Nothing to do if it isn't supposed to be an array. + + arrayForm = VerifySetOptions ( arrayForm, 0 ); // Set the implicit array bits. + XMP_Node * newArray = new XMP_Node ( dcSchema, currProp->name.c_str(), arrayForm ); + dcSchema->children[propNum] = newArray; + + if ( currProp->value.empty() ) { // Don't add an empty item, leave the array empty. + + delete ( currProp ); + + } else { + + newArray->children.push_back ( currProp ); + currProp->parent = newArray; + currProp->name = kXMP_ArrayItemName; + + if ( XMP_ArrayIsAltText ( arrayForm ) && (! (currProp->options & kXMP_PropHasLang)) ) { + XMP_Node * newLang = new XMP_Node ( currProp, "xml:lang", "x-default", kXMP_PropIsQualifier ); + currProp->options |= (kXMP_PropHasQualifiers | kXMP_PropHasLang); + if ( currProp->qualifiers.empty() ) { // *** Need a util? + currProp->qualifiers.push_back ( newLang ); + } else { + currProp->qualifiers.insert ( currProp->qualifiers.begin(), newLang ); + } + } + + } + + } + +} // NormalizeDCArrays + + +// ------------------------------------------------------------------------------------------------- +// CompareAliasedSubtrees +// ---------------------- + +// *** Change to do some alias-specific setup, then use CompareSubtrees. One special case for +// *** aliases is a simple to x-default alias, the options and qualifiers obviously differ. + +static void +CompareAliasedSubtrees ( XMP_Node * aliasNode, XMP_Node * baseNode, + XMPMeta::ErrorCallbackInfo & errorCallback, bool outerCall = true ) +{ + // ! The outermost call is special. The names almost certainly differ. The qualifiers (and + // ! hence options) will differ for an alias to the x-default item of a langAlt array. + + if ( (aliasNode->value != baseNode->value) || + (aliasNode->children.size() != baseNode->children.size()) ) { + // Keep things simple for now. Aliases are virtually unused, so this is very unlikely to + // happen. Recovery can be added later if it becomes important. + XMP_Error error(kXMPErr_BadXMP, "Mismatch between alias and base nodes"); + errorCallback.NotifyClient ( kXMPErrSev_OperationFatal, error ); + } + + if ( ! outerCall ) { + if ( (aliasNode->name != baseNode->name) || + (aliasNode->options != baseNode->options) || + (aliasNode->qualifiers.size() != baseNode->qualifiers.size()) ) { + // Keep things simple for now. Aliases are virtually unused, so this is very unlikely to + // happen. Recovery can be added later if it becomes important. + XMP_Error error(kXMPErr_BadXMP, "Mismatch between alias and base nodes"); + errorCallback.NotifyClient ( kXMPErrSev_OperationFatal, error ); + } + } + + for ( size_t childNum = 0, childLim = aliasNode->children.size(); childNum < childLim; ++childNum ) { + XMP_Node * aliasChild = aliasNode->children[childNum]; + XMP_Node * baseChild = baseNode->children[childNum]; + CompareAliasedSubtrees ( aliasChild, baseChild, errorCallback, false ); + } + + for ( size_t qualNum = 0, qualLim = aliasNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) { + XMP_Node * aliasQual = aliasNode->qualifiers[qualNum]; + XMP_Node * baseQual = baseNode->qualifiers[qualNum]; + CompareAliasedSubtrees ( aliasQual, baseQual, errorCallback, false ); + } + +} // CompareAliasedSubtrees + + +// ------------------------------------------------------------------------------------------------- +// TransplantArrayItemAlias +// ------------------------ + +static void +TransplantArrayItemAlias ( XMP_Node * oldParent, size_t oldNum, XMP_Node * newParent, + XMPMeta::ErrorCallbackInfo & errorCallback ) +{ + XMP_Node * childNode = oldParent->children[oldNum]; + + if ( newParent->options & kXMP_PropArrayIsAltText ) { + if ( childNode->options & kXMP_PropHasLang ) { + // Keep things simple for now. Aliases are virtually unused, so this is very unlikely to + // happen. Recovery can be added later if it becomes important. + XMP_Error error(kXMPErr_BadXMP, "Alias to x-default already has a language qualifier"); + errorCallback.NotifyClient ( kXMPErrSev_OperationFatal, error ); // *** Allow x-default. + } + childNode->options |= (kXMP_PropHasQualifiers | kXMP_PropHasLang); + XMP_Node * langQual = new XMP_Node ( childNode, "xml:lang", "x-default", kXMP_PropIsQualifier ); // *** AddLangQual util? + if ( childNode->qualifiers.empty() ) { + childNode->qualifiers.push_back ( langQual ); + } else { + childNode->qualifiers.insert ( childNode->qualifiers.begin(), langQual ); + } + } + + oldParent->children.erase ( oldParent->children.begin() + oldNum ); + childNode->name = kXMP_ArrayItemName; + childNode->parent = newParent; + if ( newParent->children.empty() ) { + newParent->children.push_back ( childNode ); + } else { + newParent->children.insert ( newParent->children.begin(), childNode ); + } + +} // TransplantArrayItemAlias + + +// ------------------------------------------------------------------------------------------------- +// TransplantNamedAlias +// -------------------- + +static void +TransplantNamedAlias ( XMP_Node * oldParent, size_t oldNum, XMP_Node * newParent, XMP_VarString & newName ) +{ + XMP_Node * childNode = oldParent->children[oldNum]; + + oldParent->children.erase ( oldParent->children.begin() + oldNum ); + childNode->name = newName; + childNode->parent = newParent; + newParent->children.push_back ( childNode ); + +} // TransplantNamedAlias + + +// ------------------------------------------------------------------------------------------------- +// MoveExplicitAliases +// ------------------- + +static void +MoveExplicitAliases ( XMP_Node * tree, XMP_OptionBits parseOptions, XMPMeta::ErrorCallbackInfo & errorCallback ) +{ + tree->options ^= kXMP_PropHasAliases; + const bool strictAliasing = ((parseOptions & kXMP_StrictAliasing) != 0); + + // Visit all of the top level nodes looking for aliases. If there is no base, transplant the + // alias subtree. If there is a base and strict aliasing is on, make sure the alias and base + // subtrees match. + + // ! Use "while" loops not "for" loops since both the schema and property loops can remove the + // ! current item from the vector being traversed. And don't increment the counter for a delete. + + size_t schemaNum = 0; + while ( schemaNum < tree->children.size() ) { + XMP_Node * currSchema = tree->children[schemaNum]; + + size_t propNum = 0; + while ( propNum < currSchema->children.size() ) { + XMP_Node * currProp = currSchema->children[propNum]; + if ( ! (currProp->options & kXMP_PropIsAlias) ) { + ++propNum; + continue; + } + currProp->options ^= kXMP_PropIsAlias; + + // Find the base path, look for the base schema and root node. + + XMP_AliasMapPos aliasPos = sRegisteredAliasMap->find ( currProp->name ); + XMP_Assert ( aliasPos != sRegisteredAliasMap->end() ); + XMP_ExpandedXPath & basePath = aliasPos->second; + XMP_OptionBits arrayOptions = (basePath[kRootPropStep].options & kXMP_PropArrayFormMask); + + XMP_Node * baseSchema = FindSchemaNode ( tree, basePath[kSchemaStep].step.c_str(), kXMP_CreateNodes ); + if ( baseSchema->options & kXMP_NewImplicitNode ) baseSchema->options ^= kXMP_NewImplicitNode; + XMP_Node * baseNode = FindChildNode ( baseSchema, basePath[kRootPropStep].step.c_str(), kXMP_ExistingOnly ); + + if ( baseNode == 0 ) { + + if ( basePath.size() == 2 ) { + // A top-to-top alias, transplant the property. + TransplantNamedAlias ( currSchema, propNum, baseSchema, basePath[kRootPropStep].step ); + } else { + // An alias to an array item, create the array and transplant the property. + baseNode = new XMP_Node ( baseSchema, basePath[kRootPropStep].step.c_str(), arrayOptions ); + baseSchema->children.push_back ( baseNode ); + TransplantArrayItemAlias ( currSchema, propNum, baseNode, errorCallback ); + } + + } else if ( basePath.size() == 2 ) { + + // The base node does exist and this is a top-to-top alias. Check for conflicts if + // strict aliasing is on. Remove and delete the alias subtree. + if ( strictAliasing ) CompareAliasedSubtrees ( currProp, baseNode, errorCallback ); + currSchema->children.erase ( currSchema->children.begin() + propNum ); + delete currProp; + + } else { + + // This is an alias to an array item and the array exists. Look for the aliased item. + // Then transplant or check & delete as appropriate. + + XMP_Node * itemNode = 0; + if ( arrayOptions & kXMP_PropArrayIsAltText ) { + XMP_Index xdIndex = LookupLangItem ( baseNode, *xdefaultName ); + if ( xdIndex != -1 ) itemNode = baseNode->children[xdIndex]; + } else if ( ! baseNode->children.empty() ) { + itemNode = baseNode->children[0]; + } + + if ( itemNode == 0 ) { + TransplantArrayItemAlias ( currSchema, propNum, baseNode, errorCallback ); + } else { + if ( strictAliasing ) CompareAliasedSubtrees ( currProp, itemNode, errorCallback ); + currSchema->children.erase ( currSchema->children.begin() + propNum ); + delete currProp; + } + + } + + } // Property loop + + // Increment the counter or remove an empty schema node. + if ( currSchema->children.size() > 0 ) { + ++schemaNum; + } else { + delete tree->children[schemaNum]; // ! Delete the schema node itself. + tree->children.erase ( tree->children.begin() + schemaNum ); + } + + } // Schema loop + +} // MoveExplicitAliases + + +// ------------------------------------------------------------------------------------------------- +// FixGPSTimeStamp +// --------------- + +static void +FixGPSTimeStamp ( XMP_Node * exifSchema, XMP_Node * gpsDateTime ) +{ + XMP_DateTime binGPSStamp; + try { + XMPUtils::ConvertToDate ( gpsDateTime->value.c_str(), &binGPSStamp ); + } catch ( ... ) { + return; // Don't let a bad date stop other things. + } + if ( (binGPSStamp.year != 0) || (binGPSStamp.month != 0) || (binGPSStamp.day != 0) ) return; + + XMP_Node * otherDate = FindChildNode ( exifSchema, "exif:DateTimeOriginal", kXMP_ExistingOnly ); + if ( otherDate == 0 ) otherDate = FindChildNode ( exifSchema, "exif:DateTimeDigitized", kXMP_ExistingOnly ); + if ( otherDate == 0 ) return; + + XMP_DateTime binOtherDate; + try { + XMPUtils::ConvertToDate ( otherDate->value.c_str(), &binOtherDate ); + } catch ( ... ) { + return; // Don't let a bad date stop other things. + } + + binGPSStamp.year = binOtherDate.year; + binGPSStamp.month = binOtherDate.month; + binGPSStamp.day = binOtherDate.day; + + XMPUtils::ConvertFromDate ( binGPSStamp, &gpsDateTime->value ); + +} // FixGPSTimeStamp + + +// ------------------------------------------------------------------------------------------------- +// MigrateAudioCopyright +// --------------------- +// +// The initial support for WAV files mapped a legacy ID3 audio copyright into a new xmpDM:copyright +// property. This is special case code to migrate that into dc:rights['x-default']. The rules: +// +// 1. If there is no dc:rights array, or an empty array - +// Create one with dc:rights['x-default'] set from double linefeed and xmpDM:copyright. +// +// 2. If there is a dc:rights array but it has no x-default item - +// Create an x-default item as a copy of the first item then apply rule #3. +// +// 3. If there is a dc:rights array with an x-default item, look for a double linefeed in the value. +// A. If no double linefeed, compare the x-default value to the xmpDM:copyright value. +// A1. If they match then leave the x-default value alone. +// A2. Otherwise, append a double linefeed and the xmpDM:copyright value to the x-default value. +// B. If there is a double linefeed, compare the trailing text to the xmpDM:copyright value. +// B1. If they match then leave the x-default value alone. +// B2. Otherwise, replace the trailing x-default text with the xmpDM:copyright value. +// +// 4. In all cases, delete the xmpDM:copyright property. + +static void +MigrateAudioCopyright ( XMPMeta * xmp, XMP_Node * dmCopyright ) +{ + + try { + + std::string & dmValue = dmCopyright->value; + static const char * kDoubleLF = "\xA\xA"; + + XMP_Node * dcSchema = FindSchemaNode ( &xmp->tree, kXMP_NS_DC, kXMP_CreateNodes ); + XMP_Node * dcRightsArray = FindChildNode ( dcSchema, "dc:rights", kXMP_ExistingOnly ); + + if ( (dcRightsArray == 0) || dcRightsArray->children.empty() ) { + + // 1. No dc:rights array, create from double linefeed and xmpDM:copyright. + dmValue.insert ( 0, kDoubleLF ); + xmp->SetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", dmValue.c_str(), 0 ); + + } else { + + std::string xdefaultStr ( "x-default" ); + + XMP_Index xdIndex = LookupLangItem ( dcRightsArray, xdefaultStr ); + + if ( xdIndex < 0 ) { + // 2. No x-default item, create from the first item. + XMP_StringPtr firstValue = dcRightsArray->children[0]->value.c_str(); + xmp->SetLocalizedText ( kXMP_NS_DC, "rights", "", "x-default", firstValue, 0 ); + xdIndex = LookupLangItem ( dcRightsArray, xdefaultStr ); + } + + // 3. Look for a double linefeed in the x-default value. + XMP_Assert ( xdIndex == 0 ); + std::string & defaultValue = dcRightsArray->children[xdIndex]->value; + XMP_Index lfPos = defaultValue.find ( kDoubleLF ); + + if ( lfPos < 0 ) { + + // 3A. No double LF, compare whole values. + if ( dmValue != defaultValue ) { + // 3A2. Append the xmpDM:copyright to the x-default item. + defaultValue += kDoubleLF; + defaultValue += dmValue; + } + + } else { + + // 3B. Has double LF, compare the tail. + if ( defaultValue.compare ( lfPos+2, std::string::npos, dmValue ) != 0 ) { + // 3B2. Replace the x-default tail. + defaultValue.replace ( lfPos+2, std::string::npos, dmValue ); + } + + } + + } + + // 4. Get rid of the xmpDM:copyright. + xmp->DeleteProperty ( kXMP_NS_DM, "copyright" ); + + } catch ( ... ) { + // Don't let failures (like a bad dc:rights form) stop other cleanup. + } + +} // MigrateAudioCopyright + + +// ------------------------------------------------------------------------------------------------- +// RepairAltText +// ------------- +// +// Make sure that the array is well-formed AltText. Each item must be simple and have an xml:lang +// qualifier. If repairs are needed, keep simple non-empty items by adding the xml:lang. + +static void +RepairAltText ( XMP_Node & tree, XMP_StringPtr schemaNS, XMP_StringPtr arrayName ) +{ + XMP_Node * schemaNode = FindSchemaNode ( &tree, schemaNS, kXMP_ExistingOnly ); + if ( schemaNode == 0 ) return; + + XMP_Node * arrayNode = FindChildNode ( schemaNode, arrayName, kXMP_ExistingOnly ); + if ( (arrayNode == 0) || XMP_ArrayIsAltText ( arrayNode->options ) ) return; // Already OK. + + if ( ! XMP_PropIsArray ( arrayNode->options ) ) return; // ! Not even an array, leave it alone. + // *** Should probably change simple values to LangAlt with 'x-default' item. + + arrayNode->options |= (kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate | kXMP_PropArrayIsAltText); + + for ( int i = arrayNode->children.size()-1; i >= 0; --i ) { // ! Need a signed index type. + + XMP_Node * currChild = arrayNode->children[i]; + + if ( ! XMP_PropIsSimple ( currChild->options ) ) { + + // Delete non-simple children. + delete ( currChild ); + arrayNode->children.erase ( arrayNode->children.begin() + i ); + + } else if ( ! XMP_PropHasLang ( currChild->options ) ) { + + if ( currChild->value.empty() ) { + + // Delete empty valued children that have no xml:lang. + delete ( currChild ); + arrayNode->children.erase ( arrayNode->children.begin() + i ); + + } else { + + // Add an xml:lang qualifier with the value "x-repair". + XMP_Node * repairLang = new XMP_Node ( currChild, "xml:lang", "x-repair", kXMP_PropIsQualifier ); + if ( currChild->qualifiers.empty() ) { + currChild->qualifiers.push_back ( repairLang ); + } else { + currChild->qualifiers.insert ( currChild->qualifiers.begin(), repairLang ); + } + currChild->options |= (kXMP_PropHasQualifiers | kXMP_PropHasLang); + + } + + } + + } + +} // RepairAltText + + +// ------------------------------------------------------------------------------------------------- +// TouchUpDataModel +// ---------------- + +static void +TouchUpDataModel ( XMPMeta * xmp, XMPMeta::ErrorCallbackInfo & errorCallback ) +{ + XMP_Node & tree = xmp->tree; + + // Do special case touch ups for certain schema. + + XMP_Node * currSchema = 0; + + currSchema = FindSchemaNode ( &tree, kXMP_NS_EXIF, kXMP_ExistingOnly ); + if ( currSchema != 0 ) { + + // Do a special case fix for exif:GPSTimeStamp. + XMP_Node * gpsDateTime = FindChildNode ( currSchema, "exif:GPSTimeStamp", kXMP_ExistingOnly ); + if ( gpsDateTime != 0 ) FixGPSTimeStamp ( currSchema, gpsDateTime ); + + // *** Should probably have RepairAltText change simple values to LangAlt with 'x-default' item. + // *** For now just do this for exif:UserComment, the one case we know about, late in cycle fix. + XMP_Node * userComment = FindChildNode ( currSchema, "exif:UserComment", kXMP_ExistingOnly ); + if ( (userComment != 0) && XMP_PropIsSimple ( userComment->options ) ) { + XMP_Node * newChild = new XMP_Node ( userComment, kXMP_ArrayItemName, + userComment->value.c_str(), userComment->options ); + newChild->qualifiers.swap ( userComment->qualifiers ); + if ( ! XMP_PropHasLang ( newChild->options ) ) { + XMP_Node * langQual = new XMP_Node ( newChild, "xml:lang", "x-default", kXMP_PropIsQualifier ); + newChild->qualifiers.insert ( newChild->qualifiers.begin(), langQual ); + newChild->options |= (kXMP_PropHasQualifiers | kXMP_PropHasLang); + } + userComment->value.erase(); + userComment->options = kXMP_PropArrayFormMask; // ! Happens to have all the right bits. + userComment->children.push_back ( newChild ); + } + + } + + currSchema = FindSchemaNode ( &tree, kXMP_NS_DM, kXMP_ExistingOnly ); + if ( currSchema != 0 ) { + // Do a special case migration of xmpDM:copyright to dc:rights['x-default']. Do this before + // the dc: touch up since it can affect the dc: schema. + XMP_Node * dmCopyright = FindChildNode ( currSchema, "xmpDM:copyright", kXMP_ExistingOnly ); + if ( dmCopyright != 0 ) MigrateAudioCopyright ( xmp, dmCopyright ); + } + + currSchema = FindSchemaNode ( &tree, kXMP_NS_DC, kXMP_ExistingOnly ); + if ( currSchema != 0 ) { + // Do a special case fix for dc:subject, make sure it is an unordered array. + XMP_Node * dcSubject = FindChildNode ( currSchema, "dc:subject", kXMP_ExistingOnly ); + if ( dcSubject != 0 ) { + XMP_OptionBits keepMask = ~(kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate | kXMP_PropArrayIsAltText); + dcSubject->options &= keepMask; // Make sure any ordered array bits are clear. + } + } + + // Fix any broken AltText arrays that we know about. + + RepairAltText ( tree, kXMP_NS_DC, "dc:description" ); // ! Note inclusion of prefixes for direct node lookup! + RepairAltText ( tree, kXMP_NS_DC, "dc:rights" ); + RepairAltText ( tree, kXMP_NS_DC, "dc:title" ); + RepairAltText ( tree, kXMP_NS_XMP_Rights, "xmpRights:UsageTerms" ); + RepairAltText ( tree, kXMP_NS_EXIF, "exif:UserComment" ); + + // Tweak old XMP: Move an instance ID from rdf:about to the xmpMM:InstanceID property. An old + // instance ID usually looks like "uuid:bac965c4-9d87-11d9-9a30-000d936b79c4", plus InDesign + // 3.0 wrote them like "bac965c4-9d87-11d9-9a30-000d936b79c4". If the name looks like a UUID + // simply move it to xmpMM:InstanceID, don't worry about any existing xmpMM:InstanceID. Both + // will only be present when a newer file with the xmpMM:InstanceID property is updated by an + // old app that uses rdf:about. + + if ( ! tree.name.empty() ) { + + bool nameIsUUID = false; + XMP_StringPtr nameStr = tree.name.c_str(); + + if ( XMP_LitNMatch ( nameStr, "uuid:", 5 ) ) { + + nameIsUUID = true; + + } else if ( tree.name.size() == 36 ) { + + nameIsUUID = true; // ! Assume true, we'll set it to false below if not. + for ( int i = 0; i < 36; ++i ) { + char ch = nameStr[i]; + if ( ch == '-' ) { + if ( (i == 8) || (i == 13) || (i == 18) || (i == 23) ) continue; + nameIsUUID = false; + break; + } else { + if ( (('0' <= ch) && (ch <= '9')) || (('a' <= ch) && (ch <= 'z')) ) continue; + nameIsUUID = false; + break; + } + } + + } + + if ( nameIsUUID ) { + + XMP_ExpandedXPath expPath; + ExpandXPath ( kXMP_NS_XMP_MM, "InstanceID", &expPath ); + XMP_Node * idNode = FindNode ( &tree, expPath, kXMP_CreateNodes, 0 ); + if ( idNode == 0 ) XMP_Throw ( "Failure creating xmpMM:InstanceID", kXMPErr_InternalFailure ); + + idNode->options = 0; // Clobber any existing xmpMM:InstanceID. + idNode->value = tree.name; + idNode->RemoveChildren(); + idNode->RemoveQualifiers(); + + tree.name.erase(); + + } + + } + +} // TouchUpDataModel + + +// ------------------------------------------------------------------------------------------------- +// DetermineInputEncoding +// ---------------------- +// +// Try to determine the character encoding, making a guess if the input is too short. We make some +// simplifying assumtions: the first character must be U+FEFF or ASCII, U+0000 is not allowed. The +// XML 1.1 spec is even more strict, UTF-16 XML documents must begin with U+FEFF, and the first +// "real" character must be '<'. Ignoring the XML declaration, the first XML character could be '<', +// space, tab, CR, or LF. +// +// The possible input sequences are: +// +// Cases with U+FEFF +// EF BB BF -- - UTF-8 +// FE FF -- -- - Big endian UTF-16 +// 00 00 FE FF - Big endian UTF 32 +// FF FE 00 00 - Little endian UTF-32 +// FF FE -- -- - Little endian UTF-16 +// +// Cases with ASCII +// nn mm -- -- - UTF-8 - +// 00 00 00 nn - Big endian UTF-32 +// 00 nn -- -- - Big endian UTF-16 +// nn 00 00 00 - Little endian UTF-32 +// nn 00 -- -- - Little endian UTF-16 +// +// ! We don't check for full patterns, or for errors. We just check enough to determine what the +// ! only possible (or reasonable) case would be. + +static XMP_OptionBits +DetermineInputEncoding ( const XMP_Uns8 * buffer, size_t length ) +{ + if ( length < 2 ) return kXMP_EncodeUTF8; + + XMP_Uns8 * uniChar = (XMP_Uns8*)buffer; // ! Make sure comparisons are unsigned. + + if ( uniChar[0] == 0 ) { + + // These cases are: + // 00 nn -- -- - Big endian UTF-16 + // 00 00 00 nn - Big endian UTF-32 + // 00 00 FE FF - Big endian UTF 32 + + if ( (length < 4) || (uniChar[1] != 0) ) return kXMP_EncodeUTF16Big; + return kXMP_EncodeUTF32Big; + + } else if ( uniChar[0] < 0x80 ) { + + // These cases are: + // nn mm -- -- - UTF-8, includes EF BB BF case + // nn 00 00 00 - Little endian UTF-32 + // nn 00 -- -- - Little endian UTF-16 + + if ( uniChar[1] != 0 ) return kXMP_EncodeUTF8; + if ( (length < 4) || (uniChar[2] != 0) ) return kXMP_EncodeUTF16Little; + return kXMP_EncodeUTF32Little; + + } else { + + // These cases are: + // EF BB BF -- - UTF-8 + // FE FF -- -- - Big endian UTF-16 + // FF FE 00 00 - Little endian UTF-32 + // FF FE -- -- - Little endian UTF-16 + + if ( uniChar[0] == 0xEF ) return kXMP_EncodeUTF8; + if ( uniChar[0] == 0xFE ) return kXMP_EncodeUTF16Big; + if ( (length < 4) || (uniChar[2] != 0) ) return kXMP_EncodeUTF16Little; + return kXMP_EncodeUTF32Little; + + } + +} // DetermineInputEncoding + + +// ------------------------------------------------------------------------------------------------- +// CountUTF8 +// --------- +// +// Look for a valid multi-byte UTF-8 sequence and return its length. Returns 0 for an invalid UTF-8 +// sequence. Returns a negative value for a partial valid sequence at the end of the buffer. +// +// The checking is not strict. We simply count the number of high order 1 bits in the first byte, +// then look for n-1 following bytes whose high order 2 bits are 1 and 0. We do not check for a +// minimal length representation of the codepoint, or that the codepoint is defined by Unicode. + +static int +CountUTF8 ( const XMP_Uns8 * charStart, const XMP_Uns8 * bufEnd ) +{ + XMP_Assert ( charStart < bufEnd ); // Catch this in debug builds. + if ( charStart >= bufEnd ) return 0; // Don't run-on in release builds. + if ( (*charStart & 0xC0) != 0xC0 ) return 0; // Must have at least 2 high bits set. + + int byteCount = 2; + XMP_Uns8 firstByte = *charStart; + for ( firstByte = firstByte << 2; (firstByte & 0x80) != 0; firstByte = firstByte << 1 ) ++byteCount; + + if ( (charStart + byteCount) > bufEnd ) return -byteCount; + + for ( int i = 1; i < byteCount; ++i ) { + if ( (charStart[i] & 0xC0) != 0x80 ) return 0; + } + + return byteCount; + +} // CountUTF8 + + +// ------------------------------------------------------------------------------------------------- +// CountControlEscape +// ------------------ +// +// Look for a numeric escape sequence for a "prohibited" ASCII control character. These are 0x7F, +// and the range 0x00..0x1F except for tab/LF/CR. Return 0 if this is definitely not a numeric +// escape, the length of the escape if found, or a negative value for a partial escape. + +static int +CountControlEscape ( const XMP_Uns8 * escStart, const XMP_Uns8 * bufEnd ) +{ + XMP_Assert ( escStart < bufEnd ); // Catch this in debug builds. + if ( escStart >= bufEnd ) return 0; // Don't run-on in release builds. + XMP_Assert ( *escStart == '&' ); + + size_t tailLen = bufEnd - escStart; + if ( tailLen < 5 ) return -1; // Don't need a more thorough check, we'll catch it on the next pass. + + if ( strncmp ( (char*)escStart, "&#x", 3 ) != 0 ) return 0; + + XMP_Uns8 escValue = 0; + const XMP_Uns8 * escPos = escStart + 3; + + if ( ('0' <= *escPos) && (*escPos <= '9') ) { + escValue = *escPos - '0'; + ++escPos; + } else if ( ('A' <= *escPos) && (*escPos <= 'F') ) { + escValue = *escPos - 'A' + 10; + ++escPos; + } else if ( ('a' <= *escPos) && (*escPos <= 'f') ) { + escValue = *escPos - 'a' + 10; + ++escPos; + } + + if ( ('0' <= *escPos) && (*escPos <= '9') ) { + escValue = (escValue << 4) + (*escPos - '0'); + ++escPos; + } else if ( ('A' <= *escPos) && (*escPos <= 'F') ) { + escValue = (escValue << 4) + (*escPos - 'A' + 10); + ++escPos; + } else if ( ('a' <= *escPos) && (*escPos <= 'f') ) { + escValue = (escValue << 4) + (*escPos - 'a' + 10); + ++escPos; + } + + if ( escPos == bufEnd ) return -1; // Partial escape. + if ( *escPos != ';' ) return 0; + + size_t escLen = escPos - escStart + 1; + if ( escLen < 5 ) return 0; // ! Catch "&#x;". + + if ( (escValue == kTab) || (escValue == kLF) || (escValue == kCR) ) return 0; // An allowed escape. + + return escLen; // Found a full "prohibited" numeric escape. + +} // CountControlEscape + + +// ------------------------------------------------------------------------------------------------- +// ProcessUTF8Portion +// ------------------ +// +// Early versions of the XMP spec mentioned allowing ISO Latin-1 input. There are also problems with +// some clients placing ASCII control characters within XMP values. This is an XML problem, the XML +// spec only allows tab (0x09), LF (0x0A), and CR (0x0D) from the 0x00..0x1F range. As a concession +// to this we scan 8-bit input for byte sequences that are not valid UTF-8 or in the 0x00..0x1F +// range and replace each byte as follows: +// 0x00..0x1F - Replace with a space, except for tab, CR, and LF. +// 0x7F - Replace with a space. This is ASCII Delete, not allowed by ISO Latin-1. +// 0x80..0x9F - Replace with the UTF-8 for a corresponding Unicode character. +// 0xA0..0XFF - Replace with the UTF-8 for a corresponding Unicode character. +// +// The 0x80..0x9F range is not defined by Latin-1. But the Windows 1252 code page defines these and +// is otherwise the same as Latin-1. +// +// For at least historical compatibility reasons we also find and replace singly escaped ASCII +// control characters. The Expat parser we're using does not allow numeric escapes like "". +// The XML spec is clear that raw controls are not allowed (in the RestrictedChar set), but it isn't +// as clear about numeric escapes for them. At any rate, Expat complains, so we treat the numeric +// escapes like raw characters and replace them with a space. +// +// We check for 1 or 2 hex digits (" " or " ") and upper or lower case (" " or " "). +// The full escape sequence is 5 or 6 bytes. + +static size_t +ProcessUTF8Portion ( XMLParserAdapter * xmlParser, + const XMP_Uns8 * buffer, + size_t length, + bool last ) +{ + const XMP_Uns8 * bufEnd = buffer + length; + + const XMP_Uns8 * spanStart = buffer; + const XMP_Uns8 * spanEnd; + + for ( spanEnd = spanStart; spanEnd < bufEnd; ++spanEnd ) { + + if ( (0x20 <= *spanEnd) && (*spanEnd <= 0x7E) && (*spanEnd != '&') ) continue; // A regular ASCII character. + + if ( *spanEnd >= 0x80 ) { + + // See if this is a multi-byte UTF-8 sequence, or a Latin-1 character to replace. + + int uniLen = CountUTF8 ( spanEnd, bufEnd ); + + if ( uniLen > 0 ) { + + // A valid UTF-8 character, keep it as-is. + spanEnd += uniLen - 1; // ! The loop increment will put back the +1. + + } else if ( (uniLen < 0) && (! last) ) { + + // Have a partial UTF-8 character at the end of the buffer and more input coming. + xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false ); + return (spanEnd - buffer); + + } else { + + // Not a valid UTF-8 sequence. Replace the first byte with the Latin-1 equivalent. + xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false ); + const char * replacement = kReplaceLatin1 [ *spanEnd - 0x80 ]; + xmlParser->ParseBuffer ( replacement, strlen ( replacement ), false ); + spanStart = spanEnd + 1; // ! The loop increment will do "spanEnd = spanStart". + + } + + } else if ( (*spanEnd < 0x20) || (*spanEnd == 0x7F) ) { + + // Replace ASCII controls other than tab, LF, and CR with a space. + + if ( (*spanEnd == kTab) || (*spanEnd == kLF) || (*spanEnd == kCR) ) continue; + + xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false ); + xmlParser->ParseBuffer ( " ", 1, false ); + spanStart = spanEnd + 1; // ! The loop increment will do "spanEnd = spanStart". + + } else { + + // See if this is a numeric escape sequence for a prohibited ASCII control. + + XMP_Assert ( *spanEnd == '&' ); + int escLen = CountControlEscape ( spanEnd, bufEnd ); + + if ( escLen < 0 ) { + + // Have a partial numeric escape in this buffer, wait for more input. + if ( last ) continue; // No more buffers, not an escape, absorb as normal input. + xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false ); + return (spanEnd - buffer); + + } else if ( escLen > 0 ) { + + // Have a complete numeric escape to replace. + xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false ); + xmlParser->ParseBuffer ( " ", 1, false ); + spanStart = spanEnd + escLen; + spanEnd = spanStart - 1; // ! The loop continuation will increment spanEnd! + + } + + } + + } + + XMP_Assert ( spanEnd == bufEnd ); + + if ( spanStart < bufEnd ) xmlParser->ParseBuffer ( spanStart, (spanEnd - spanStart), false ); + if ( last ) xmlParser->ParseBuffer ( " ", 1, true ); + + return length; + +} // ProcessUTF8Portion + + +// ------------------------------------------------------------------------------------------------- +// ProcessXMLBuffer +// ---------------- +// +// Process one buffer of XML. Returns false if this input is put into the pending input buffer. + +bool XMPMeta::ProcessXMLBuffer ( XMP_StringPtr buffer, XMP_StringLen xmpSize, bool lastClientCall ) +{ + + // Determine the character encoding before doing any real parsing. This is needed to do the + // 8-bit special processing. This has to be checked on every call, not just the first, in + // order to handle the edge case of single byte buffers. + + XMLParserAdapter & parser = *this->xmlParser; + + if ( parser.charEncoding == XMP_OptionBits(-1) ) { + + if ( (parser.pendingCount == 0) && (xmpSize >= kXMLPendingInputMax) ) { + + // This ought to be the common case, the first buffer is big enough. + parser.charEncoding = DetermineInputEncoding ( (XMP_Uns8*)buffer, xmpSize ); + + } else { + + // Try to fill the pendingInput buffer before calling DetermineInputEncoding. + + size_t pendingOverlap = kXMLPendingInputMax - parser.pendingCount; + if ( pendingOverlap > xmpSize ) pendingOverlap = xmpSize; + + memcpy ( &parser.pendingInput[parser.pendingCount], buffer, pendingOverlap ); // AUDIT: Count is safe. + buffer += pendingOverlap; + xmpSize -= pendingOverlap; + parser.pendingCount += pendingOverlap; + + if ( (! lastClientCall) && (parser.pendingCount < kXMLPendingInputMax) ) return false; // Wait for the next buffer. + parser.charEncoding = DetermineInputEncoding ( parser.pendingInput, parser.pendingCount ); + + #if Trace_ParsingHackery + fprintf ( stderr, "XMP Character encoding is %d\n", parser.charEncoding ); + #endif + + } + + } + + // We have the character encoding. Process UTF-16 and UTF-32 as is. UTF-8 needs special + // handling to take care of things like ISO Latin-1 or unescaped ASCII controls. + + XMP_Assert ( parser.charEncoding != XMP_OptionBits(-1) ); + + if ( parser.charEncoding != kXMP_EncodeUTF8 ) { + + if ( parser.pendingCount > 0 ) { + // Might have pendingInput from the above portion to determine the character encoding. + parser.ParseBuffer ( parser.pendingInput, parser.pendingCount, false ); + } + parser.ParseBuffer ( buffer, xmpSize, lastClientCall ); + + } else { + + #if Trace_ParsingHackery + fprintf ( stderr, "Parsing %d bytes @ %.8X, %s, %d pending, context: %.8s\n", + xmpSize, buffer, (lastClientCall ? "last" : "not last"), parser.pendingCount, buffer ); + #endif + + // The UTF-8 processing is a bit complex due to the need to tolerate ISO Latin-1 input. + // This is done by scanning the input for byte sequences that are not valid UTF-8, + // assuming they are Latin-1 characters in the range 0x80..0xFF. This requires saving a + // pending input buffer to handle partial UTF-8 sequences at the end of a buffer. + + while ( parser.pendingCount > 0 ) { + + // We've got some leftover input, process it first then continue with the current + // buffer. Try to fill the pendingInput buffer before parsing further. We use a loop + // for wierd edge cases like a 2 byte input buffer, using 1 byte for pendingInput, + // then having a partial UTF-8 end and need to absorb more. + + size_t pendingOverlap = kXMLPendingInputMax - parser.pendingCount; + if ( pendingOverlap > xmpSize ) pendingOverlap = xmpSize; + + memcpy ( &parser.pendingInput[parser.pendingCount], buffer, pendingOverlap ); // AUDIT: Count is safe. + parser.pendingCount += pendingOverlap; + buffer += pendingOverlap; + xmpSize -= pendingOverlap; + + if ( (! lastClientCall) && (parser.pendingCount < kXMLPendingInputMax) ) return false; // Wait for the next buffer. + size_t bytesDone = ProcessUTF8Portion ( xmlParser, parser.pendingInput, parser.pendingCount, lastClientCall ); + size_t bytesLeft = parser.pendingCount - bytesDone; + + #if Trace_ParsingHackery + fprintf ( stderr, " ProcessUTF8Portion handled %d pending bytes\n", bytesDone ); + #endif + + if ( bytesDone == parser.pendingCount ) { + + // Done with all of the pending input, move on to the current buffer. + parser.pendingCount = 0; + + } else if ( bytesLeft <= pendingOverlap ) { + + // The leftover pending input all came from the current buffer. Exit this loop. + buffer -= bytesLeft; + xmpSize += bytesLeft; + parser.pendingCount = 0; + + } else if ( xmpSize > 0 ) { + + // Pull more of the current buffer into the pending input and try again. + // Backup by this pass's overlap so the loop entry code runs OK. + parser.pendingCount -= pendingOverlap; + buffer -= pendingOverlap; + xmpSize += pendingOverlap; + + } else { + + // There is no more of the current buffer. Wait for more. Partial sequences at + // the end of the last buffer should be treated as Latin-1 by ProcessUTF8Portion. + XMP_Assert ( ! lastClientCall ); + parser.pendingCount = bytesLeft; + memcpy ( &parser.pendingInput[0], &parser.pendingInput[bytesDone], bytesLeft ); // AUDIT: Count is safe. + return false; // Wait for the next buffer. + + } + + } + + // Done with the pending input, process the current buffer. + + size_t bytesDone = ProcessUTF8Portion ( xmlParser, (XMP_Uns8*)buffer, xmpSize, lastClientCall ); + + #if Trace_ParsingHackery + fprintf ( stderr, " ProcessUTF8Portion handled %d additional bytes\n", bytesDone ); + #endif + + if ( bytesDone < xmpSize ) { + + XMP_Assert ( ! lastClientCall ); + size_t bytesLeft = xmpSize - bytesDone; + if ( bytesLeft > kXMLPendingInputMax ) XMP_Throw ( "Parser bytesLeft too large", kXMPErr_InternalFailure ); + + memcpy ( parser.pendingInput, &buffer[bytesDone], bytesLeft ); // AUDIT: Count is safe. + parser.pendingCount = bytesLeft; + return false; // Wait for the next buffer. + + } + + } + + return true; // This buffer has been processed. + +} // ProcessXMLBuffer + + +// ------------------------------------------------------------------------------------------------- +// ProcessXMLTree +// -------------- + +void XMPMeta::ProcessXMLTree ( XMP_OptionBits options ) +{ + + #if XMP_DebugBuild && DumpXMLParseTree + if ( this->xmlParser->parseLog == 0 ) this->xmlParser->parseLog = stdout; + DumpXMLTree ( this->xmlParser->parseLog, this->xmlParser->tree, 0 ); + #endif + + const XML_Node * xmlRoot = FindRootNode ( *this->xmlParser, options ); + + if ( xmlRoot != 0 ) { + + this->ProcessRDF ( *xmlRoot, options ); + + NormalizeDCArrays ( &this->tree ); + if ( this->tree.options & kXMP_PropHasAliases ) MoveExplicitAliases ( &this->tree, options, this->errorCallback ); + TouchUpDataModel ( this, this->errorCallback ); + + // Delete empty schema nodes. Do this last, other cleanup can make empty schema. + size_t schemaNum = 0; + while ( schemaNum < this->tree.children.size() ) { + XMP_Node * currSchema = this->tree.children[schemaNum]; + if ( currSchema->children.size() > 0 ) { + ++schemaNum; + } else { + delete this->tree.children[schemaNum]; // ! Delete the schema node itself. + this->tree.children.erase ( this->tree.children.begin() + schemaNum ); + } + } + + } + +} // ProcessXMLTree + + +// ------------------------------------------------------------------------------------------------- +// ParseFromBuffer +// --------------- +// +// Although most clients will probably parse everything in one call, we have a buffered API model +// and need to support even the extreme case of 1 byte at a time parsing. This is considerably +// complicated by some special cases for 8-bit input. Because of this, the first thing we do is +// determine whether the input is 8-bit, UTF-16, or UTF-32. +// +// Both the 8-bit special cases and the encoding determination are easier to do with 8 bytes or more +// of input. The XMLParserAdapter class has a pending-input buffer for this. At the start of parsing +// we (might) try to fill this buffer before determining the input character encoding. After that, +// we (might) use this buffer with the current input to simplify the logic in Process8BitInput. The +// "(might)" part means that we don't actually use the pending-input buffer unless we have to. In +// particular, the common case of single-buffer parsing won't use it. + +void +XMPMeta::ParseFromBuffer ( XMP_StringPtr buffer, + XMP_StringLen xmpSize, + XMP_OptionBits options ) +{ + if ( (buffer == 0) && (xmpSize != 0) ) XMP_Throw ( "Null parse buffer", kXMPErr_BadParam ); + if ( xmpSize == kXMP_UseNullTermination ) xmpSize = strlen ( buffer ); + + const bool lastClientCall = ((options & kXMP_ParseMoreBuffers) == 0); // *** Could use FlagIsSet & FlagIsClear macros. + + if ( this->xmlParser == 0 ) { + this->tree.ClearNode(); // Make sure the target XMP object is totally empty. + if ( (xmpSize == 0) && lastClientCall ) return; // Tolerate empty parse. Expat complains if there are no XML elements. + this->xmlParser = XMP_NewExpatAdapter ( ExpatAdapter::kUseGlobalNamespaces ); + this->xmlParser->SetErrorCallback ( &this->errorCallback ); + } + + try { // Cleanup the tree and xmlParser if anything fails. + + bool done = this->ProcessXMLBuffer ( buffer, xmpSize, lastClientCall ); + if ( ! done ) return; // Wait for the next buffer. + + if ( lastClientCall ) { + this->ProcessXMLTree ( options ); + delete this->xmlParser; + this->xmlParser = 0; + } + + } catch ( ... ) { + + delete this->xmlParser; + this->xmlParser = 0; + throw; + + } + +} // ParseFromBuffer + +// ================================================================================================= diff --git a/source/lib/xmp_core/XMPMeta-Serialize.cpp b/source/lib/xmp_core/XMPMeta-Serialize.cpp new file mode 100644 index 0000000..3c46269 --- /dev/null +++ b/source/lib/xmp_core/XMPMeta-Serialize.cpp @@ -0,0 +1,1396 @@ +// ================================================================================================= +// Copyright 2003-2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// +// Adobe patent application tracking #P435, entitled 'Unique markers to simplify embedding data of +// one format in a file with a different format', inventors: Sean Parent, Greg Gilley. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! This must be the first include! +#include "XMPCore_Impl.hpp" + +#include "XMPMeta.hpp" + +#include "public/include/XMP_Version.h" +#include "UnicodeInlines.incl_cpp" +#include "UnicodeConversions.hpp" + +#include "md5.h" + +#if XMP_DebugBuild + #include +#endif + +using namespace std; + +#if XMP_WinBuild + #pragma warning ( disable : 4533 ) // initialization of '...' is skipped by 'goto ...' + #pragma warning ( disable : 4702 ) // unreachable code + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) +#endif + + +// *** Use the XMP_PropIsXyz (Schema, Simple, Struct, Array, ...) macros +// *** Add debug codegen checks, e.g. that typical masking operations really work +// *** Change all uses of strcmp and strncmp to XMP_LitMatch and XMP_LitNMatch + + +// ================================================================================================= +// Local Types and Constants +// ========================= + +static const char * kPacketHeader = ""; +static const char * kPacketTrailer = ""; // ! The w/r is at [size-4]. + +static const char * kTXMP_SchemaGroup = "XMP_SchemaGroup"; + +static const char * kRDF_XMPMetaStart = ""; +static const char * kRDF_RDFEnd = ""; + +static const char * kRDF_SchemaStart = ""; + +static const char * kRDF_StructStart = ""; +static const char * kRDF_StructEnd = ""; + +static const char * kRDF_BagStart = ""; +static const char * kRDF_BagEnd = ""; + +static const char * kRDF_SeqStart = ""; +static const char * kRDF_SeqEnd = ""; + +static const char * kRDF_AltStart = ""; +static const char * kRDF_AltEnd = ""; + +static const char * kRDF_ItemStart = ""; +static const char * kRDF_ItemEnd = ""; + +static const char * kRDF_ValueStart = ""; +static const char * kRDF_ValueEnd = ""; + + +// ================================================================================================= +// Static Variables +// ================ + + +// ================================================================================================= +// Local Utilities +// =============== + + +// ------------------------------------------------------------------------------------------------- +// EstimateRDFSize +// --------------- + +// *** Pull the strlen(kXyz) calls into constants. + +static size_t +EstimateRDFSize ( const XMP_Node * currNode, XMP_Index indent, size_t indentLen ) +{ + size_t outputLen = 2 * (indent*indentLen + currNode->name.size() + 4); // The property element tags. + + if ( ! currNode->qualifiers.empty() ) { + // This node has qualifiers, assume it is written using rdf:value and estimate the qualifiers. + + indent += 2; // Everything else is indented inside the rdf:Description element. + outputLen += 2 * ((indent-1)*indentLen + strlen(kRDF_StructStart) + 2); // The rdf:Description tags. + outputLen += 2 * (indent*indentLen + strlen(kRDF_ValueStart) + 2); // The rdf:value tags. + + for ( size_t qualNum = 0, qualLim = currNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) { + const XMP_Node * currQual = currNode->qualifiers[qualNum]; + outputLen += EstimateRDFSize ( currQual, indent, indentLen ); + } + + } + + if ( currNode->options & kXMP_PropValueIsStruct ) { + indent += 1; + outputLen += 2 * (indent*indentLen + strlen(kRDF_StructStart) + 2); // The rdf:Description tags. + } else if ( currNode->options & kXMP_PropValueIsArray ) { + indent += 2; + outputLen += 2 * ((indent-1)*indentLen + strlen(kRDF_BagStart) + 2); // The rdf:Bag/Seq/Alt tags. + outputLen += 2 * currNode->children.size() * (strlen(kRDF_ItemStart) + 2); // The rdf:li tags, indent counted in children. + } else if ( ! (currNode->options & kXMP_SchemaNode) ) { + outputLen += currNode->value.size(); // This is a leaf value node. + } + + for ( size_t childNum = 0, childLim = currNode->children.size(); childNum < childLim; ++childNum ) { + const XMP_Node * currChild = currNode->children[childNum]; + outputLen += EstimateRDFSize ( currChild, indent+1, indentLen ); + } + + return outputLen; + +} // EstimateRDFSize + + +// ------------------------------------------------------------------------------------------------- +// DeclareOneNamespace +// ------------------- + +static void +DeclareOneNamespace ( XMP_StringPtr nsPrefix, + XMP_StringPtr nsURI, + XMP_VarString & usedNS, // ! A catenation of the prefixes with colons. + XMP_VarString & outputStr, + XMP_StringPtr newline, + XMP_StringPtr indentStr, + XMP_Index indent ) +{ + XMP_VarString boundedPrefix = ":"; + boundedPrefix += nsPrefix; + size_t nsPos = usedNS.find ( boundedPrefix ); + + if ( nsPos == XMP_VarString::npos ) { + + outputStr += newline; + for ( ; indent > 0; --indent ) outputStr += indentStr; + outputStr += "xmlns:"; + outputStr += nsPrefix; + outputStr[outputStr.size()-1] = '='; // Change the colon to =. + outputStr += '"'; + outputStr += nsURI; + outputStr += '"'; + + usedNS += nsPrefix; + + } + +} // DeclareOneNamespace + + +// ------------------------------------------------------------------------------------------------- +// DeclareElemNamespace +// -------------------- + +static void +DeclareElemNamespace ( const XMP_VarString & elemName, + XMP_VarString & usedNS, + XMP_VarString & outputStr, + XMP_StringPtr newline, + XMP_StringPtr indentStr, + XMP_Index indent ) +{ + size_t colonPos = elemName.find ( ':' ); + + if ( colonPos != XMP_VarString::npos ) { + XMP_VarString nsPrefix ( elemName.substr ( 0, colonPos+1 ) ); + XMP_StringPtr nsURI; + bool nsFound = sRegisteredNamespaces->GetURI ( nsPrefix.c_str(), &nsURI, 0 ); + XMP_Enforce ( nsFound ); + DeclareOneNamespace ( nsPrefix.c_str(), nsURI, usedNS, outputStr, newline, indentStr, indent ); + } + +} // DeclareElemNamespace + + +// ------------------------------------------------------------------------------------------------- +// DeclareUsedNamespaces +// --------------------- + +static void +DeclareUsedNamespaces ( const XMP_Node * currNode, + XMP_VarString & usedNS, + XMP_VarString & outputStr, + XMP_StringPtr newline, + XMP_StringPtr indentStr, + XMP_Index indent ) +{ + + if ( currNode->options & kXMP_SchemaNode ) { + // The schema node name is the URI, the value is the prefix. + DeclareOneNamespace ( currNode->value.c_str(), currNode->name.c_str(), usedNS, outputStr, newline, indentStr, indent ); + } else if ( currNode->options & kXMP_PropValueIsStruct ) { + for ( size_t fieldNum = 0, fieldLim = currNode->children.size(); fieldNum < fieldLim; ++fieldNum ) { + const XMP_Node * currField = currNode->children[fieldNum]; + DeclareElemNamespace ( currField->name, usedNS, outputStr, newline, indentStr, indent ); + } + } + + for ( size_t childNum = 0, childLim = currNode->children.size(); childNum < childLim; ++childNum ) { + const XMP_Node * currChild = currNode->children[childNum]; + DeclareUsedNamespaces ( currChild, usedNS, outputStr, newline, indentStr, indent ); + } + + for ( size_t qualNum = 0, qualLim = currNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) { + const XMP_Node * currQual = currNode->qualifiers[qualNum]; + DeclareElemNamespace ( currQual->name, usedNS, outputStr, newline, indentStr, indent ); + DeclareUsedNamespaces ( currQual, usedNS, outputStr, newline, indentStr, indent ); + } + +} // DeclareUsedNamespaces + +// ------------------------------------------------------------------------------------------------- +// EmitRDFArrayTag +// --------------- + +enum { + kIsStartTag = true, + kIsEndTag = false +}; + +static void +EmitRDFArrayTag ( XMP_OptionBits arrayForm, + XMP_VarString & outputStr, + XMP_StringPtr newline, + XMP_StringPtr indentStr, + XMP_Index indent, + XMP_Index arraySize, + bool isStartTag ) +{ + if ( (! isStartTag) && (arraySize == 0) ) return; + + for ( XMP_Index level = indent; level > 0; --level ) outputStr += indentStr; + if ( isStartTag ) { + outputStr += "', and ASCII controls (tab, LF, CR). In +// addition, '"' is escaped for attributes. For efficiency, this is done in a double loop. The outer +// loop makes sure the whole value is processed. The inner loop does a contiguous unescaped run +// followed by one escaped character (if we're not at the end). +// +// We depend on parsing and SetProperty logic to make sure there are no invalid ASCII controls in +// the XMP values. The XML spec only allows tab, LF, and CR. Others are not even allowed as +// numeric escape sequences. + +enum { + kForAttribute = true, + kForElement = false +}; + +static void +AppendNodeValue ( XMP_VarString & outputStr, const XMP_VarString & value, bool forAttribute ) +{ + + unsigned char * runStart = (unsigned char *) value.c_str(); + unsigned char * runLimit = runStart + value.size(); + unsigned char * runEnd; + unsigned char ch; + + while ( runStart < runLimit ) { + + for ( runEnd = runStart; runEnd < runLimit; ++runEnd ) { + ch = *runEnd; + if ( forAttribute && (ch == '"') ) break; + if ( (ch < 0x20) || (ch == '&') || (ch == '<') || (ch == '>') ) break; + } + + outputStr.append ( (char *) runStart, (runEnd - runStart) ); + + if ( runEnd < runLimit ) { + + if ( ch < 0x20 ) { + + XMP_Assert ( (ch == kTab) || (ch == kLF) || (ch == kCR) ); + + char hexBuf[16]; + memcpy ( hexBuf, "&#xn;", 6 ); // AUDIT: Length of "&#xn;" is 5, hexBuf size is 16. + hexBuf[3] = kHexDigits[ch&0xF]; + outputStr.append ( hexBuf, 5 ); + + } else { + + if ( ch == '"' ) { + outputStr += """; + } else if ( ch == '<' ) { + outputStr += "<"; + } else if ( ch == '>' ) { + outputStr += ">"; + } else { + XMP_Assert ( ch == '&' ); + outputStr += "&"; + } + + } + + ++runEnd; + + } + + runStart = runEnd; + + } + +} // AppendNodeValue + + +// ------------------------------------------------------------------------------------------------- +// CanBeRDFAttrProp +// ---------------- + +static bool +CanBeRDFAttrProp ( const XMP_Node * propNode ) +{ + + if ( propNode->name[0] == '[' ) return false; + if ( ! propNode->qualifiers.empty() ) return false; + if ( propNode->options & kXMP_PropValueIsURI ) return false; + if ( propNode->options & kXMP_PropCompositeMask ) return false; + + return true; + +} // CanBeRDFAttrProp + + +// ------------------------------------------------------------------------------------------------- +// IsRDFAttrQualifier +// ------------------ + +static XMP_StringPtr sAttrQualifiers[] = { "xml:lang", "rdf:resource", "rdf:ID", "rdf:bagID", "rdf:nodeID", "" }; + +static bool +IsRDFAttrQualifier ( XMP_VarString qualName ) +{ + + for ( size_t i = 0; *sAttrQualifiers[i] != 0; ++i ) { + if ( qualName == sAttrQualifiers[i] ) return true; + } + + return false; + +} // IsRDFAttrQualifier + + +// ------------------------------------------------------------------------------------------------- +// StartOuterRDFDescription +// ------------------------ +// +// Start the outer rdf:Description element, including all needed xmlns attributes. Leave the element +// open so that the compact form can add proprtty attributes. + +static void +StartOuterRDFDescription ( const XMP_Node & xmpTree, + XMP_VarString & outputStr, + XMP_StringPtr newline, + XMP_StringPtr indentStr, + XMP_Index baseIndent ) +{ + + // Begin the outer rdf:Description start tag. + + for ( XMP_Index level = baseIndent+2; level > 0; --level ) outputStr += indentStr; + outputStr += kRDF_SchemaStart; + outputStr += '"'; + outputStr += xmpTree.name; + outputStr += '"'; + + // Write all necessary xmlns attributes. + + XMP_VarString usedNS; + usedNS.reserve ( 400 ); // The predefined prefixes add up to about 320 bytes. + usedNS = ":xml:rdf:"; + + for ( size_t schema = 0, schemaLim = xmpTree.children.size(); schema != schemaLim; ++schema ) { + const XMP_Node * currSchema = xmpTree.children[schema]; + DeclareUsedNamespaces ( currSchema, usedNS, outputStr, newline, indentStr, baseIndent+4 ); + } + +} // StartOuterRDFDescription + +// ------------------------------------------------------------------------------------------------- +// SerializeCanonicalRDFProperty +// ----------------------------- +// +// Recursively handles the "value" for a node. It does not matter if it is a top level property, a +// field of a struct, or an item of an array. The indent is that for the property element. An +// xml:lang qualifier is written as an attribute of the property start tag, not by itself forcing +// the qualified property form. The patterns below mostly ignore attribute qualifiers like xml:lang. +// Except for the one struct case, attribute qualifiers don't affect the output form. +// +// value +// +// (If no rdf:resource qualifier) +// +// ... Fields, same forms as top level properties +// +// +// +// +// +// +// or Seq or Alt +// ... Array items as rdf:li elements, same forms as top level properties +// +// +// +// +// +// ... Property "value" following the unqualified forms ... +// ... Qualifiers looking like named struct fields +// +// + +enum { kUseCanonicalRDF = true, kUseAdobeVerboseRDF = false }; +enum { kEmitAsRDFValue = true, kEmitAsNormalValue = false }; + +static void +SerializeCanonicalRDFProperty ( const XMP_Node * propNode, + XMP_VarString & outputStr, + XMP_StringPtr newline, + XMP_StringPtr indentStr, + XMP_Index indent, + bool useCanonicalRDF, + bool emitAsRDFValue ) +{ + XMP_Index level; + bool emitEndTag = true; + bool indentEndTag = true; + + XMP_OptionBits propForm = propNode->options & kXMP_PropCompositeMask; + + // ------------------------------------------------------------------------------------------ + // Determine the XML element name. Open the start tag with the name and attribute qualifiers. + + XMP_StringPtr elemName = propNode->name.c_str(); + if ( emitAsRDFValue ) { + elemName= "rdf:value"; + } else if ( *elemName == '[' ) { + elemName = "rdf:li"; + } + + for ( level = indent; level > 0; --level ) outputStr += indentStr; + outputStr += '<'; + outputStr += elemName; + + bool hasGeneralQualifiers = false; + bool hasRDFResourceQual = false; + + for ( size_t qualNum = 0, qualLim = propNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) { + const XMP_Node * currQual = propNode->qualifiers[qualNum]; + if ( ! IsRDFAttrQualifier ( currQual->name ) ) { + hasGeneralQualifiers = true; + } else { + if ( currQual->name == "rdf:resource" ) hasRDFResourceQual = true; + if ( ! emitAsRDFValue ) { + outputStr += ' '; + outputStr += currQual->name; + outputStr += "=\""; + AppendNodeValue ( outputStr, currQual->value, kForAttribute ); + outputStr += '"'; + } + } + } + + // -------------------------------------------------------- + // Process the property according to the standard patterns. + + if ( hasGeneralQualifiers && (! emitAsRDFValue) ) { + + // ----------------------------------------------------------------------------------------- + // This node has general, non-attribute, qualifiers. Emit using the qualified property form. + // ! The value is output by a recursive call ON THE SAME NODE with emitAsRDFValue set. + + if ( hasRDFResourceQual ) { + XMP_Throw ( "Can't mix rdf:resource and general qualifiers", kXMPErr_BadRDF ); + } + + if ( ! useCanonicalRDF ) { + outputStr += " rdf:parseType=\"Resource\">"; + outputStr += newline; + } else { + outputStr += '>'; + outputStr += newline; + indent += 1; + for ( level = indent; level > 0; --level ) outputStr += indentStr; + outputStr += ""; + outputStr += newline; + } + + SerializeCanonicalRDFProperty ( propNode, outputStr, newline, indentStr, indent+1, + useCanonicalRDF, kEmitAsRDFValue ); + + for ( size_t qualNum = 0, qualLim = propNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) { + const XMP_Node * currQual = propNode->qualifiers[qualNum]; + if ( IsRDFAttrQualifier ( currQual->name ) ) continue; + SerializeCanonicalRDFProperty ( currQual, outputStr, newline, indentStr, indent+1, + useCanonicalRDF, kEmitAsNormalValue ); + } + + if ( useCanonicalRDF ) { + for ( level = indent; level > 0; --level ) outputStr += indentStr; + outputStr += ""; + outputStr += newline; + indent -= 1; + } + + } else { + + // -------------------------------------------------------------------- + // This node has no general qualifiers. Emit using an unqualified form. + + if ( propForm == 0 ) { + + // -------------------------- + // This is a simple property. + + if ( propNode->options & kXMP_PropValueIsURI ) { + outputStr += " rdf:resource=\""; + AppendNodeValue ( outputStr, propNode->value, kForAttribute ); + outputStr += "\"/>"; + outputStr += newline; + emitEndTag = false; + } else if ( propNode->value.empty() ) { + outputStr += "/>"; + outputStr += newline; + emitEndTag = false; + } else { + outputStr += '>'; + AppendNodeValue ( outputStr, propNode->value, kForElement ); + indentEndTag = false; + } + + } else if ( propForm & kXMP_PropValueIsArray ) { + + // This is an array. + outputStr += '>'; + outputStr += newline; + EmitRDFArrayTag ( propForm, outputStr, newline, indentStr, indent+1, propNode->children.size(), kIsStartTag ); + if ( XMP_ArrayIsAltText(propNode->options) ) NormalizeLangArray ( (XMP_Node*)propNode ); + for ( size_t childNum = 0, childLim = propNode->children.size(); childNum < childLim; ++childNum ) { + const XMP_Node * currChild = propNode->children[childNum]; + SerializeCanonicalRDFProperty ( currChild, outputStr, newline, indentStr, indent+2, + useCanonicalRDF, kEmitAsNormalValue ); + } + EmitRDFArrayTag ( propForm, outputStr, newline, indentStr, indent+1, propNode->children.size(), kIsEndTag ); + + + } else if ( ! hasRDFResourceQual ) { + + // This is a "normal" struct, use the nested field element form form. + XMP_Assert ( propForm & kXMP_PropValueIsStruct ); + if ( propNode->children.size() == 0 ) { + if ( ! useCanonicalRDF ) { + outputStr += " rdf:parseType=\"Resource\"/>"; + outputStr += newline; + emitEndTag = false; + } else { + outputStr += '>'; + outputStr += newline; + for ( level = indent+1; level > 0; --level ) outputStr += indentStr; + outputStr += ""; + outputStr += newline; + } + } else { + if ( ! useCanonicalRDF ) { + outputStr += " rdf:parseType=\"Resource\">"; + outputStr += newline; + } else { + outputStr += '>'; + outputStr += newline; + indent += 1; + for ( level = indent; level > 0; --level ) outputStr += indentStr; + outputStr += ""; + outputStr += newline; + } + for ( size_t childNum = 0, childLim = propNode->children.size(); childNum < childLim; ++childNum ) { + const XMP_Node * currChild = propNode->children[childNum]; + SerializeCanonicalRDFProperty ( currChild, outputStr, newline, indentStr, indent+1, + useCanonicalRDF, kEmitAsNormalValue ); + } + if ( useCanonicalRDF ) { + for ( level = indent; level > 0; --level ) outputStr += indentStr; + outputStr += ""; + outputStr += newline; + indent -= 1; + } + } + + } else { + + // This is a struct with an rdf:resource attribute, use the "empty property element" form. + XMP_Assert ( propForm & kXMP_PropValueIsStruct ); + for ( size_t childNum = 0, childLim = propNode->children.size(); childNum < childLim; ++childNum ) { + const XMP_Node * currChild = propNode->children[childNum]; + if ( ! CanBeRDFAttrProp ( currChild ) ) { + XMP_Throw ( "Can't mix rdf:resource and complex fields", kXMPErr_BadRDF ); + } + outputStr += newline; + for ( level = indent+1; level > 0; --level ) outputStr += indentStr; + outputStr += ' '; + outputStr += currChild->name; + outputStr += "=\""; + outputStr += currChild->value; + outputStr += '"'; + } + outputStr += "/>"; + outputStr += newline; + emitEndTag = false; + + } + + } + + // ---------------------------------- + // Emit the property element end tag. + + if ( emitEndTag ) { + if ( indentEndTag ) for ( level = indent; level > 0; --level ) outputStr += indentStr; + outputStr += "'; + outputStr += newline; + } + +} // SerializeCanonicalRDFProperty + + +// ------------------------------------------------------------------------------------------------- +// SerializeCanonicalRDFSchemas +// ---------------------------- +// +// Each schema's properties are written to the single rdf:Description element. All of the necessary +// namespaces are declared in the rdf:Description element. The baseIndent is the base level for the +// entire serialization, that of the x:xmpmeta element. An xml:lang qualifier is written as an +// attribute of the property start tag, not by itself forcing the qualified property form. +// +// +// +// ... The actual properties of the schema, see SerializeCanonicalRDFProperty +// +// ... If alias comments are wanted +// +// + +static void +SerializeCanonicalRDFSchemas ( const XMP_Node & xmpTree, + XMP_VarString & outputStr, + XMP_StringPtr newline, + XMP_StringPtr indentStr, + XMP_Index baseIndent, + bool useCanonicalRDF ) +{ + + StartOuterRDFDescription ( xmpTree, outputStr, newline, indentStr, baseIndent ); + + if ( xmpTree.children.size() > 0 ) { + outputStr += ">"; + outputStr += newline; + } else { + outputStr += "/>"; + outputStr += newline; + return; // ! Done if there are no XMP properties. + } + + for ( size_t schemaNum = 0, schemaLim = xmpTree.children.size(); schemaNum < schemaLim; ++schemaNum ) { + const XMP_Node * currSchema = xmpTree.children[schemaNum]; + for ( size_t propNum = 0, propLim = currSchema->children.size(); propNum < propLim; ++propNum ) { + const XMP_Node * currProp = currSchema->children[propNum]; + SerializeCanonicalRDFProperty ( currProp, outputStr, newline, indentStr, baseIndent+3, + useCanonicalRDF, kEmitAsNormalValue ); + } + } + + // Write the rdf:Description end tag. + for ( XMP_Index level = baseIndent+2; level > 0; --level ) outputStr += indentStr; + outputStr += kRDF_SchemaEnd; + outputStr += newline; + +} // SerializeCanonicalRDFSchemas + + +// ------------------------------------------------------------------------------------------------- +// SerializeCompactRDFAttrProps +// ---------------------------- +// +// Write each of the parent's simple unqualified properties as an attribute. Returns true if all +// of the properties are written as attributes. + +static bool +SerializeCompactRDFAttrProps ( const XMP_Node * parentNode, + XMP_VarString & outputStr, + XMP_StringPtr newline, + XMP_StringPtr indentStr, + XMP_Index indent ) +{ + size_t prop, propLim; + bool allAreAttrs = true; + + for ( prop = 0, propLim = parentNode->children.size(); prop != propLim; ++prop ) { + + const XMP_Node * currProp = parentNode->children[prop]; + if ( ! CanBeRDFAttrProp ( currProp ) ) { + allAreAttrs = false; + continue; + } + + outputStr += newline; + for ( XMP_Index level = indent; level > 0; --level ) outputStr += indentStr; + outputStr += currProp->name; + outputStr += "=\""; + AppendNodeValue ( outputStr, currProp->value, kForAttribute ); + outputStr += '"'; + + } + + return allAreAttrs; + +} // SerializeCompactRDFAttrProps + + +// ------------------------------------------------------------------------------------------------- +// SerializeCompactRDFElemProps +// ---------------------------- +// +// Recursively handles the "value" for a node that must be written as an RDF property element. It +// does not matter if it is a top level property, a field of a struct, or an item of an array. The +// indent is that for the property element. The patterns bwlow ignore attribute qualifiers such as +// xml:lang, they don't affect the output form. +// +// +// +// +// ... The fields as elements, if none are simple and unqualified +// +// +// +// +// ... The compound or qualified fields as elements +// +// +// +// +// or Seq or Alt +// ... Array items as rdf:li elements, same forms as top level properties +// +// +// +// +// ... Property "value" following the unqualified forms ... +// ... Qualifiers looking like named struct fields +// + +// *** Consider numbered array items, but has compatibility problems. +// *** Consider qualified form with rdf:Description and attributes. + +static void +SerializeCompactRDFElemProps ( const XMP_Node * parentNode, + XMP_VarString & outputStr, + XMP_StringPtr newline, + XMP_StringPtr indentStr, + XMP_Index indent ) +{ + XMP_Index level; + + for ( size_t prop = 0, propLim = parentNode->children.size(); prop != propLim; ++prop ) { + + const XMP_Node * propNode = parentNode->children[prop]; + if ( CanBeRDFAttrProp ( propNode ) ) continue; + + bool emitEndTag = true; + bool indentEndTag = true; + + XMP_OptionBits propForm = propNode->options & kXMP_PropCompositeMask; + + // ----------------------------------------------------------------------------------- + // Determine the XML element name, write the name part of the start tag. Look over the + // qualifiers to decide on "normal" versus "rdf:value" form. Emit the attribute + // qualifiers at the same time. + + XMP_StringPtr elemName = propNode->name.c_str(); + if ( *elemName == '[' ) elemName = "rdf:li"; + + for ( level = indent; level > 0; --level ) outputStr += indentStr; + outputStr += '<'; + outputStr += elemName; + + bool hasGeneralQualifiers = false; + bool hasRDFResourceQual = false; + + for ( size_t qualNum = 0, qualLim = propNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) { + const XMP_Node * currQual = propNode->qualifiers[qualNum]; + if ( ! IsRDFAttrQualifier ( currQual->name ) ) { + hasGeneralQualifiers = true; + } else { + if ( currQual->name == "rdf:resource" ) hasRDFResourceQual = true; + outputStr += ' '; + outputStr += currQual->name; + outputStr += "=\""; + AppendNodeValue ( outputStr, currQual->value, kForAttribute ); + outputStr += '"'; + } + } + + // -------------------------------------------------------- + // Process the property according to the standard patterns. + + if ( hasGeneralQualifiers ) { + + // ------------------------------------------------------------------------------------- + // The node has general qualifiers, ones that can't be attributes on a property element. + // Emit using the qualified property pseudo-struct form. The value is output by a call + // to SerializeCanonicalRDFProperty with emitAsRDFValue set. + + // *** We're losing compactness in the calls to SerializeCanonicalRDFProperty. + // *** Should refactor to have SerializeCompactRDFProperty that does one node. + + outputStr += " rdf:parseType=\"Resource\">"; + outputStr += newline; + + SerializeCanonicalRDFProperty ( propNode, outputStr, newline, indentStr, indent+1, + kUseAdobeVerboseRDF, kEmitAsRDFValue ); + + size_t qualNum = 0; + size_t qualLim = propNode->qualifiers.size(); + if ( propNode->options & kXMP_PropHasLang ) ++qualNum; + + for ( ; qualNum < qualLim; ++qualNum ) { + const XMP_Node * currQual = propNode->qualifiers[qualNum]; + SerializeCanonicalRDFProperty ( currQual, outputStr, newline, indentStr, indent+1, + kUseAdobeVerboseRDF, kEmitAsNormalValue ); + } + + } else { + + // -------------------------------------------------------------------- + // This node has only attribute qualifiers. Emit as a property element. + + if ( propForm == 0 ) { + + // -------------------------- + // This is a simple property. + + if ( propNode->options & kXMP_PropValueIsURI ) { + outputStr += " rdf:resource=\""; + AppendNodeValue ( outputStr, propNode->value, kForAttribute ); + outputStr += "\"/>"; + outputStr += newline; + emitEndTag = false; + } else if ( propNode->value.empty() ) { + outputStr += "/>"; + outputStr += newline; + emitEndTag = false; + } else { + outputStr += '>'; + AppendNodeValue ( outputStr, propNode->value, kForElement ); + indentEndTag = false; + } + + } else if ( propForm & kXMP_PropValueIsArray ) { + + // ----------------- + // This is an array. + + outputStr += '>'; + outputStr += newline; + EmitRDFArrayTag ( propForm, outputStr, newline, indentStr, indent+1, propNode->children.size(), kIsStartTag ); + + if ( XMP_ArrayIsAltText(propNode->options) ) NormalizeLangArray ( (XMP_Node*)propNode ); + SerializeCompactRDFElemProps ( propNode, outputStr, newline, indentStr, indent+2 ); + + EmitRDFArrayTag ( propForm, outputStr, newline, indentStr, indent+1, propNode->children.size(), kIsEndTag ); + + } else { + + // ---------------------- + // This must be a struct. + + XMP_Assert ( propForm & kXMP_PropValueIsStruct ); + + bool hasAttrFields = false; + bool hasElemFields = false; + + size_t field, fieldLim; + for ( field = 0, fieldLim = propNode->children.size(); field != fieldLim; ++field ) { + XMP_Node * currField = propNode->children[field]; + if ( CanBeRDFAttrProp ( currField ) ) { + hasAttrFields = true; + if ( hasElemFields ) break; // No sense looking further. + } else { + hasElemFields = true; + if ( hasAttrFields ) break; // No sense looking further. + } + } + + if ( hasRDFResourceQual && hasElemFields ) { + XMP_Throw ( "Can't mix rdf:resource qualifier and element fields", kXMPErr_BadRDF ); + } + + if ( propNode->children.size() == 0 ) { + + // Catch an empty struct as a special case. The case below would emit an empty + // XML element, which gets reparsed as a simple property with an empty value. + outputStr += " rdf:parseType=\"Resource\"/>"; + outputStr += newline; + emitEndTag = false; + + } else if ( ! hasElemFields ) { + + // All fields can be attributes, use the emptyPropertyElt form. + SerializeCompactRDFAttrProps ( propNode, outputStr, newline, indentStr, indent+1 ); + outputStr += "/>"; + outputStr += newline; + emitEndTag = false; + + } else if ( ! hasAttrFields ) { + + // All fields must be elements, use the parseTypeResourcePropertyElt form. + outputStr += " rdf:parseType=\"Resource\">"; + outputStr += newline; + SerializeCompactRDFElemProps ( propNode, outputStr, newline, indentStr, indent+1 ); + + } else { + + // Have a mix of attributes and elements, use an inner rdf:Description. + outputStr += '>'; + outputStr += newline; + for ( level = indent+1; level > 0; --level ) outputStr += indentStr; + outputStr += " 0; --level ) outputStr += indentStr; + outputStr += kRDF_StructEnd; + outputStr += newline; + + } + + } + + } + + // ---------------------------------- + // Emit the property element end tag. + + if ( emitEndTag ) { + if ( indentEndTag ) for ( level = indent; level > 0; --level ) outputStr += indentStr; + outputStr += "'; + outputStr += newline; + } + + } + +} // SerializeCompactRDFElemProps + + +// ------------------------------------------------------------------------------------------------- +// SerializeCompactRDFSchemas +// -------------------------- +// +// All properties from all schema are written in a single rdf:Description element, as are all of the +// necessary namespace declarations. The baseIndent is the base level for the entire serialization, +// that of the x:xmpmeta element. The x:xmpmeta and rdf:RDF elements have already been written. +// +// Top level simple unqualified properties are written as attributes of the (only) rdf:Description +// element. Structs, arrays, and qualified properties are written by SerializeCompactRDFElemProp. An +// xml:lang qualifier on a simple property prevents the attribute form. +// +// +// ... The remaining properties of the schema, see SerializeCompactRDFElemProps +// + +static void +SerializeCompactRDFSchemas ( const XMP_Node & xmpTree, + XMP_VarString & outputStr, + XMP_StringPtr newline, + XMP_StringPtr indentStr, + XMP_Index baseIndent ) +{ + XMP_Index level; + size_t schema, schemaLim; + + StartOuterRDFDescription ( xmpTree, outputStr, newline, indentStr, baseIndent ); + + // Write the top level "attrProps" and close the rdf:Description start tag. + bool allAreAttrs = true; + for ( schema = 0, schemaLim = xmpTree.children.size(); schema != schemaLim; ++schema ) { + const XMP_Node * currSchema = xmpTree.children[schema]; + allAreAttrs &= SerializeCompactRDFAttrProps ( currSchema, outputStr, newline, indentStr, baseIndent+3 ); + } + if ( ! allAreAttrs ) { + outputStr += ">"; + outputStr += newline; + } else { + outputStr += "/>"; + outputStr += newline; + return; // ! Done if all properties in all schema are written as attributes. + } + + // Write the remaining properties for each schema. + for ( schema = 0, schemaLim = xmpTree.children.size(); schema != schemaLim; ++schema ) { + const XMP_Node * currSchema = xmpTree.children[schema]; + SerializeCompactRDFElemProps ( currSchema, outputStr, newline, indentStr, baseIndent+3 ); + } + + // Write the rdf:Description end tag. + for ( level = baseIndent+2; level > 0; --level ) outputStr += indentStr; + outputStr += kRDF_SchemaEnd; + outputStr += newline; + +} // SerializeCompactRDFSchemas + +// ------------------------------------------------------------------------------------------------- +// SerializeAsRDF +// -------------- +// +// +// +// +// +// ... The properties, see SerializeCanonicalRDFSchema or SerializeCompactRDFSchemas +// +// +// +// + +// *** Need to strip empty arrays? +// *** Option to strip/keep empty structs? +// *** Need to verify handling of rdf:type qualifiers in canonical and compact. +// *** Need to verify round tripping of rdf:ID and similar qualifiers, see RDF 7.2.21. +// *** Check cases of rdf:resource plus explicit attr qualifiers (like xml:lang). + +static void +SerializeAsRDF ( const XMPMeta & xmpObj, + XMP_VarString & headStr, // Everything up to the padding. + XMP_VarString & tailStr, // Everything after the padding. + XMP_OptionBits options, + XMP_StringPtr newline, + XMP_StringPtr indentStr, + XMP_Index baseIndent ) +{ + const size_t treeNameLen = xmpObj.tree.name.size(); + const size_t indentLen = strlen ( indentStr ); + + // First estimate the worst case space and reserve room in the output string. This optimization + // avoids reallocating and copying the output as it grows. The initial count does not look at + // the values of properties, so it does not account for character entities, e.g. for newline. + // Since there can be a lot of these in things like the base 64 encoding of a large thumbnail, + // inflate the count by 1/4 (easy to do) to accommodate. + + // *** Need to include estimate for alias comments. + + size_t outputLen = 2 * (strlen(kPacketHeader) + strlen(kRDF_XMPMetaStart) + strlen(kRDF_RDFStart) + 3*baseIndent*indentLen); + + for ( size_t schemaNum = 0, schemaLim = xmpObj.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) { + const XMP_Node * currSchema = xmpObj.tree.children[schemaNum]; + outputLen += 2*(baseIndent+2)*indentLen + strlen(kRDF_SchemaStart) + treeNameLen + strlen(kRDF_SchemaEnd) + 2; + outputLen += EstimateRDFSize ( currSchema, baseIndent+2, indentLen ); + } + + outputLen += (outputLen >> 2); // Inflate by 1/4, an empirical fudge factor. + + // Now generate the RDF into the head string as UTF-8. + + XMP_Index level; + + std::string rdfstring; + headStr.erase(); + rdfstring.reserve ( outputLen ); + + // Write the rdf:RDF start tag. + rdfstring += kRDF_RDFStart; + rdfstring += newline; + + // Write all of the properties. + if ( options & kXMP_UseCompactFormat ) { + SerializeCompactRDFSchemas ( xmpObj.tree, rdfstring, newline, indentStr, baseIndent ); + } else { + bool useCanonicalRDF = XMP_OptionIsSet ( options, kXMP_UseCanonicalFormat ); + SerializeCanonicalRDFSchemas ( xmpObj.tree, rdfstring, newline, indentStr, baseIndent, useCanonicalRDF ); + } + + // Write the rdf:RDF end tag. + for ( level = baseIndent+1; level > 0; --level ) rdfstring += indentStr; + rdfstring += kRDF_RDFEnd; + // Write the packet header PI. + if ( ! (options & kXMP_OmitPacketWrapper) ) { + for ( level = baseIndent; level > 0; --level ) headStr += indentStr; + headStr += kPacketHeader; + headStr += newline; + } + + // Write the xmpmeta element's start tag. + if ( ! (options & kXMP_OmitXMPMetaElement) ) { + for ( level = baseIndent; level > 0; --level ) headStr += indentStr; + headStr += kRDF_XMPMetaStart; + headStr += kXMPCore_VersionMessage "\""; + std::string digestStr; + unsigned char digestBin [16]; + if (options & kXMP_IncludeRDFHash) + { + std::string hashrdf; + + { + context_md5_t ctx; + + MD5Init(&ctx); + MD5Update(&ctx, (unsigned char*)rdfstring.c_str(), (unsigned int)rdfstring.size() ); + MD5Final(digestBin, &ctx); + } + + char buffer [40]; + for ( int in = 0, out = 0; in < 16; in += 1, out += 2 ) { + XMP_Uns8 byte = digestBin[in]; + buffer[out] = kHexDigits [ byte >> 4 ]; + buffer[out+1] = kHexDigits [ byte & 0xF ]; + } + buffer[32] = 0; + digestStr.append ( buffer ); + headStr += " rdfhash=\""; + headStr += digestStr + "\""; + headStr += " merged=\"0\""; + } + headStr += ">"; + headStr += newline; + } + + for ( level = baseIndent+1; level > 0; --level ) headStr += indentStr; + headStr+= rdfstring ; + headStr += newline; + + // Write the xmpmeta end tag. + if ( ! (options & kXMP_OmitXMPMetaElement) ) { + for ( level = baseIndent; level > 0; --level ) headStr += indentStr; + headStr += kRDF_XMPMetaEnd; + headStr += newline; + } + + // Write the packet trailer PI into the tail string as UTF-8. + tailStr.erase(); + if ( ! (options & kXMP_OmitPacketWrapper) ) { + tailStr.reserve ( strlen(kPacketTrailer) + (strlen(indentStr) * baseIndent) ); + for ( level = baseIndent; level > 0; --level ) tailStr += indentStr; + tailStr += kPacketTrailer; + if ( options & kXMP_ReadOnlyPacket ) tailStr[tailStr.size()-4] = 'r'; + } + +} // SerializeAsRDF + + +// ------------------------------------------------------------------------------------------------- +// SerializeToBuffer +// ----------------- + +void +XMPMeta::SerializeToBuffer ( XMP_VarString * rdfString, + XMP_OptionBits options, + XMP_StringLen padding, + XMP_StringPtr newline, + XMP_StringPtr indentStr, + XMP_Index baseIndent ) const +{ + XMP_Enforce( rdfString != 0 ); + XMP_Assert ( (newline != 0) && (indentStr != 0) ); + rdfString->erase(); + + // Fix up some default parameters. + + enum { kDefaultPad = 2048 }; + size_t unicodeUnitSize = 1; + XMP_OptionBits charEncoding = options & kXMP_EncodingMask; + + if ( charEncoding != kXMP_EncodeUTF8 ) { + if ( options & _XMP_UTF16_Bit ) { + if ( options & _XMP_UTF32_Bit ) XMP_Throw ( "Can't use both _XMP_UTF16_Bit and _XMP_UTF32_Bit", kXMPErr_BadOptions ); + unicodeUnitSize = 2; + } else if ( options & _XMP_UTF32_Bit ) { + unicodeUnitSize = 4; + } else { + XMP_Throw ( "Can't use _XMP_LittleEndian_Bit by itself", kXMPErr_BadOptions ); + } + } + + if ( options & kXMP_OmitAllFormatting ) { + newline = " "; // ! Yes, a space for "newline". This ensures token separation. + indentStr = ""; + } else { + if ( *newline == 0 ) newline = "\xA"; // Linefeed + if ( *indentStr == 0 ) { + indentStr = " "; + if ( ! (options & kXMP_UseCompactFormat) ) indentStr = " "; + } + } + + if ( options & kXMP_ExactPacketLength ) { + if ( options & (kXMP_OmitPacketWrapper | kXMP_IncludeThumbnailPad) ) { + XMP_Throw ( "Inconsistent options for exact size serialize", kXMPErr_BadOptions ); + } + if ( (padding & (unicodeUnitSize-1)) != 0 ) { + XMP_Throw ( "Exact size must be a multiple of the Unicode element", kXMPErr_BadOptions ); + } + } else if ( options & kXMP_ReadOnlyPacket ) { + if ( options & (kXMP_OmitPacketWrapper | kXMP_IncludeThumbnailPad) ) { + XMP_Throw ( "Inconsistent options for read-only packet", kXMPErr_BadOptions ); + } + padding = 0; + } else if ( options & kXMP_OmitPacketWrapper ) { + if ( options & kXMP_IncludeThumbnailPad ) { + XMP_Throw ( "Inconsistent options for non-packet serialize", kXMPErr_BadOptions ); + } + padding = 0; + } else if ( options & kXMP_OmitXMPMetaElement ) { + if ( options & kXMP_IncludeRDFHash ) { + XMP_Throw ( "Inconsistent options for x:xmpmeta serialize", kXMPErr_BadOptions ); + } + padding = 0; + } else { + if ( padding == 0 ) { + padding = kDefaultPad * unicodeUnitSize; + } else if ( (padding >> 28) != 0 ) { + XMP_Throw ( "Outrageously large padding size", kXMPErr_BadOptions ); // Bigger than 256 MB. + } + if ( options & kXMP_IncludeThumbnailPad ) { + if ( ! this->DoesPropertyExist ( kXMP_NS_XMP, "Thumbnails" ) ) padding += (10000 * unicodeUnitSize); // *** Need a better estimate. + } + } + + // Serialize as UTF-8, then convert to UTF-16 or UTF-32 if necessary, and assemble with the padding and tail. + + std::string tailStr; + + SerializeAsRDF ( *this, *rdfString, tailStr, options, newline, indentStr, baseIndent ); + + if ( charEncoding == kXMP_EncodeUTF8 ) { + + if ( options & kXMP_ExactPacketLength ) { + size_t minSize = rdfString->size() + tailStr.size(); + if ( minSize > padding ) XMP_Throw ( "Can't fit into specified packet size", kXMPErr_BadSerialize ); + padding -= minSize; // Now the actual amount of padding to add. + } + + size_t newlineLen = strlen ( newline ); + + if ( padding < newlineLen ) { + rdfString->append ( padding, ' ' ); + } else { + padding -= newlineLen; // Write this newline last. + while ( padding >= (100 + newlineLen) ) { + rdfString->append ( 100, ' ' ); + *rdfString += newline; + padding -= (100 + newlineLen); + } + rdfString->append ( padding, ' ' ); + *rdfString += newline; + } + + *rdfString += tailStr; + + } else { + + // Need to convert the encoding. Swap the UTF-8 into a local string and convert back. Assemble everything. + + XMP_VarString utf8Str, newlineStr; + bool bigEndian = ((charEncoding & _XMP_LittleEndian_Bit) == 0); + + if ( charEncoding & _XMP_UTF16_Bit ) { + + std::string padStr ( " " ); padStr[0] = 0; // Assume big endian. + + utf8Str.swap ( *rdfString ); + ToUTF16 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), rdfString, bigEndian ); + utf8Str.swap ( tailStr ); + ToUTF16 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), &tailStr, bigEndian ); + + if ( options & kXMP_ExactPacketLength ) { + size_t minSize = rdfString->size() + tailStr.size(); + if ( minSize > padding ) XMP_Throw ( "Can't fit into specified packet size", kXMPErr_BadSerialize ); + padding -= minSize; // Now the actual amount of padding to add (in bytes). + } + + utf8Str.assign ( newline ); + ToUTF16 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), &newlineStr, bigEndian ); + size_t newlineLen = newlineStr.size(); + + if ( padding < newlineLen ) { + for ( int i = padding/2; i > 0; --i ) *rdfString += padStr; + } else { + padding -= newlineLen; // Write this newline last. + while ( padding >= (200 + newlineLen) ) { + for ( int i = 100; i > 0; --i ) *rdfString += padStr; + *rdfString += newlineStr; + padding -= (200 + newlineLen); + } + for ( int i = padding/2; i > 0; --i ) *rdfString += padStr; + *rdfString += newlineStr; + } + + *rdfString += tailStr; + + } else { + + std::string padStr ( " " ); padStr[0] = padStr[1] = padStr[2] = 0; // Assume big endian. + UTF8_to_UTF32_Proc Converter = UTF8_to_UTF32BE; + + if ( charEncoding & _XMP_LittleEndian_Bit ) { + padStr[0] = ' '; padStr[1] = padStr[2] = padStr[3] = 0; + Converter = UTF8_to_UTF32LE; + } + + utf8Str.swap ( *rdfString ); + ToUTF32 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), rdfString, bigEndian ); + utf8Str.swap ( tailStr ); + ToUTF32 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), &tailStr, bigEndian ); + + if ( options & kXMP_ExactPacketLength ) { + size_t minSize = rdfString->size() + tailStr.size(); + if ( minSize > padding ) XMP_Throw ( "Can't fit into specified packet size", kXMPErr_BadSerialize ); + padding -= minSize; // Now the actual amount of padding to add (in bytes). + } + + utf8Str.assign ( newline ); + ToUTF32 ( (UTF8Unit*)utf8Str.c_str(), utf8Str.size(), &newlineStr, bigEndian ); + size_t newlineLen = newlineStr.size(); + + if ( padding < newlineLen ) { + for ( int i = padding/4; i > 0; --i ) *rdfString += padStr; + } else { + padding -= newlineLen; // Write this newline last. + while ( padding >= (400 + newlineLen) ) { + for ( int i = 100; i > 0; --i ) *rdfString += padStr; + *rdfString += newlineStr; + padding -= (400 + newlineLen); + } + for ( int i = padding/4; i > 0; --i ) *rdfString += padStr; + *rdfString += newlineStr; + } + + *rdfString += tailStr; + + } + + } + +} // SerializeToBuffer + +// ================================================================================================= diff --git a/source/lib/xmp_core/XMPMeta.cpp b/source/lib/xmp_core/XMPMeta.cpp new file mode 100644 index 0000000..62bcb60 --- /dev/null +++ b/source/lib/xmp_core/XMPMeta.cpp @@ -0,0 +1,1381 @@ +// ================================================================================================= +// Copyright 2003 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// +// Adobe patent application tracking #P435, entitled 'Unique markers to simplify embedding data of +// one format in a file with a different format', inventors: Sean Parent, Greg Gilley. +// ================================================================================================= + +#include // For sort and stable_sort. + +#include "public/include/XMP_Environment.h" // ! This must be the first include! +#include "XMPCore_Impl.hpp" + +#include "XMPMeta.hpp" +#include "XMPIterator.hpp" +#include "XMPUtils.hpp" +#include "public/include/XMP_Version.h" +#include "UnicodeInlines.incl_cpp" +#include "UnicodeConversions.hpp" + +#include // For snprintf. + +#if XMP_DebugBuild + #include +#endif + +using namespace std; + +#if XMP_WinBuild + #pragma warning ( disable : 4533 ) // initialization of '...' is skipped by 'goto ...' + #pragma warning ( disable : 4702 ) // unreachable code + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) + #pragma warning ( disable : 4996 ) // '...' was declared deprecated +#endif + + +// *** Use the XMP_PropIsXyz (Schema, Simple, Struct, Array, ...) macros +// *** Add debug codegen checks, e.g. that typical masking operations really work +// *** Change all uses of strcmp and strncmp to XMP_LitMatch and XMP_LitNMatch + + +// ================================================================================================= +// Local Types and Constants +// ========================= + + +// ================================================================================================= +// Static Variables +// ================ + +XMP_VarString * xdefaultName = 0; // Needed in XMPMeta-Parse.cpp, MoveExplicitAliases. + +static XMPMeta::ErrorCallbackInfo sDefaultErrorCallback; + +// These are embedded version strings. + +const char * kXMPCore_EmbeddedVersion = kXMPCore_VersionMessage; +const char * kXMPCore_EmbeddedCopyright = kXMPCoreName " " kXMP_CopyrightStr; + +// ================================================================================================= +// Local Utilities +// =============== + + +// ------------------------------------------------------------------------------------------------- +// DumpNodeOptions +// --------------- + +static void +DumpNodeOptions ( XMP_OptionBits options, + XMP_TextOutputProc outProc, + void * refCon ) +{ + char buffer [32]; // Decimal of a 64 bit int is at most about 20 digits. + memset(buffer, 0, 32); + + static const char * optNames[] = { " schema", // 0x8000_0000 + " ?30", + " ?29", + " -COMMAS-", + " ?27", // 0x0800_0000 + " ?26", + " ?25", + " ?24", + " ?23", // 0x0080_0000 + " isStale", + " isDerived", + " isStable", + " ?19", // 0x0008_0000 + " isInternal", + " hasAliases", + " isAlias", + " -AFTER-", // 0x0000_8000 + " -BEFORE-", + " isCompact", + " isLangAlt", + " isAlt", // 0x0000_0800 + " isOrdered", + " isArray", + " isStruct", + " hasType", // 0x0000_0080 + " hasLang", + " isQual", + " hasQual", + " ?3", // 0x0000_0008 + " ?2", + " URI", + " ?0" }; + + if ( options == 0 ) { + + OutProcNChars ( "(0x0)", 5 ); + + } else { + + OutProcNChars ( "(0x", 3 ); + OutProcHexInt ( options ); + OutProcNChars ( " :", 2 ); + + XMP_OptionBits mask = 0x80000000; + for ( int b = 0; b < 32; ++b ) { + if ( options & mask ) OutProcLiteral ( optNames[b] ); + mask = mask >> 1; + } + OutProcNChars ( ")", 1 ); + + } + +} // DumpNodeOptions + + +// ------------------------------------------------------------------------------------------------- +// DumpPropertyTree +// ---------------- + +// *** Extract the validation code into a separate routine to call on exit in debug builds. + +static void +DumpPropertyTree ( const XMP_Node * currNode, + int indent, + size_t itemIndex, + XMP_TextOutputProc outProc, + void * refCon ) +{ + char buffer [32]; // Decimal of a 64 bit int is at most about 20 digits. + + OutProcIndent ( (size_t)indent ); + if ( itemIndex == 0 ) { + if ( currNode->options & kXMP_PropIsQualifier ) OutProcNChars ( "? ", 2 ); + DumpClearString ( currNode->name, outProc, refCon ); + } else { + OutProcNChars ( "[", 1 ); + OutProcDecInt ( itemIndex ); + OutProcNChars ( "]", 1 ); + } + + if ( ! (currNode->options & kXMP_PropCompositeMask) ) { + OutProcNChars ( " = \"", 4 ); + DumpClearString ( currNode->value, outProc, refCon ); + OutProcNChars ( "\"", 1 ); + } + + if ( currNode->options != 0 ) { + OutProcNChars ( " ", 2 ); + DumpNodeOptions ( currNode->options, outProc, refCon ); + } + + if ( currNode->options & kXMP_PropHasLang ) { + if ( currNode->qualifiers.empty() || (currNode->qualifiers[0]->name != "xml:lang") ) { + OutProcLiteral ( " ** bad lang flag **" ); + } + } + // *** Check rdf:type also. + + if ( ! (currNode->options & kXMP_PropCompositeMask) ) { + if ( ! currNode->children.empty() ) OutProcLiteral ( " ** bad children **" ); + } else if ( currNode->options & kXMP_PropValueIsArray ) { + if ( currNode->options & kXMP_PropValueIsStruct ) OutProcLiteral ( " ** bad comp flags **" ); + } else if ( (currNode->options & kXMP_PropCompositeMask) != kXMP_PropValueIsStruct ) { + OutProcLiteral ( " ** bad comp flags **" ); + } + + #if 0 // *** XMP_DebugBuild + if ( (currNode->_namePtr != currNode->name.c_str()) || + (currNode->_valuePtr != currNode->value.c_str()) ) OutProcLiteral ( " ** bad debug string **" ); + #endif + + OutProcNewline(); + + for ( size_t qualNum = 0, qualLim = currNode->qualifiers.size(); qualNum < qualLim; ++qualNum ) { + + const XMP_Node * currQual = currNode->qualifiers[qualNum]; + + if ( currQual->parent != currNode ) OutProcLiteral ( "** bad parent link => " ); + if ( currQual->name == kXMP_ArrayItemName ) OutProcLiteral ( "** bad qual name => " ); + if ( ! (currQual->options & kXMP_PropIsQualifier) ) OutProcLiteral ( "** bad qual flag => " ); + if ( currQual->name == "xml:lang" ) { + if ( (qualNum != 0) || (! (currNode->options & kXMP_PropHasLang)) ) OutProcLiteral ( "** bad lang qual => " ); + } + + DumpPropertyTree ( currQual, indent+2, 0, outProc, refCon ); + + } + + for ( size_t childNum = 0, childLim = currNode->children.size(); childNum < childLim; ++childNum ) { + + const XMP_Node * currChild = currNode->children[childNum]; + + if ( currChild->parent != currNode ) OutProcLiteral ( "** bad parent link => " ); + if ( currChild->options & kXMP_PropIsQualifier ) OutProcLiteral ( "** bad qual flag => " ); + + if ( currNode->options & kXMP_PropValueIsArray ) { + itemIndex = childNum+1; + if ( currChild->name != kXMP_ArrayItemName ) OutProcLiteral ( "** bad item name => " ); + } else { + itemIndex = 0; + if ( currChild->name == kXMP_ArrayItemName ) OutProcLiteral ( "** bad field name => " ); + } + + DumpPropertyTree ( currChild, indent+1, itemIndex, outProc, refCon ); + + } + +} // DumpPropertyTree + + +// ------------------------------------------------------------------------------------------------- +// DumpXMLTree +// ----------- + +#if DumpXMLParseTree + +static inline void PutHexByte ( FILE * log, unsigned char ch ) +{ + + fprintf ( log, "\\x" ); + if ( ch < 0x10 ) { + fprintf ( log, "%c", kHexDigits[ch] ); + } else { + fprintf ( log, "%c%c", kHexDigits[ch>>4], kHexDigits[ch&0xF] ); + } + +} // PutHexByte + +// ------------------------------------------------------------------------------------------------- + +static void PutClearString ( FILE * log, const std::string & str ) +{ + + for ( size_t i = 0; i != str.size(); ++i ) { + unsigned char ch = str[i]; + if ( (0x20 <= ch) && (ch <= 0x7F) ) { + fprintf ( log, "%c", ch ); + } else { + PutHexByte ( log, ch ); + } + } + +} // PutClearString + +// ------------------------------------------------------------------------------------------------- + +static void DumpXMLTree ( FILE * log, const XML_Node & node, int indent ) +{ + size_t i; + + #if 0 // *** XMP_DebugBuild + if ( (node._namePtr != node.name.c_str()) || + (node._valuePtr != node.value.c_str()) ) fprintf ( log, "*** bad debug string ***\n" ); + #endif + + for ( i = 0; i != (size_t)indent; ++i ) fprintf ( log, " " ); + + switch ( node.kind ) { + + case kRootNode : + fprintf ( log, "\nStart of XML tree dump\n\n" ); + if ( (indent != 0) || (! node.attrs.empty()) || + (! node.ns.empty()) || (! node.name.empty()) || (!node.value.empty()) ) fprintf ( log, " ** invalid root ** \n" ); + for ( i = 0; i < node.children.size(); ++i ) { + XMP_Uns8 kind = node.children[i]->kind; + if ( (kind == kRootNode) || (kind == kAttrNode) ) fprintf ( log, " ** invalid child ** \n" ); + DumpXMLTree ( log, *node.children[i], indent+1 ); + } + fprintf ( log, "\nEnd of XML tree dump\n" ); + break; + + case kElemNode : + fprintf ( log, "Elem %s", node.name.c_str() ); + if ( indent == 0 ) fprintf ( log, " ** invalid elem ** " ); + if ( ! node.ns.empty() ) fprintf ( log, " @ %s", node.ns.c_str() ); + fprintf ( log, "\n" ); + for ( i = 0; i < node.attrs.size(); ++i ) { + XMP_Uns8 kind = node.attrs[i]->kind; + if ( kind != kAttrNode ) fprintf ( log, " ** invalid attr ** \n" ); + DumpXMLTree ( log, *node.attrs[i], indent+2 ); + } + for ( i = 0; i < node.children.size(); ++i ) { + XMP_Uns8 kind = node.children[i]->kind; + if ( (kind == kRootNode) || (kind == kAttrNode) ) fprintf ( log, " ** invalid child ** \n" ); + DumpXMLTree ( log, *node.children[i], indent+1 ); + } + break; + + case kAttrNode : + fprintf ( log, "Attr %s", node.name.c_str() ); + if ( (indent == 0) || node.name.empty() || (! node.attrs.empty()) || (! node.children.empty()) ) fprintf ( log, " ** invalid attr ** " ); + fprintf ( log, " = \"" ); + PutClearString ( log, node.value ); + fprintf ( log, "\"" ); + if ( ! node.ns.empty() ) fprintf ( log, " @ %s", node.ns.c_str() ); + fprintf ( log, "\n" ); + break; + + case kCDataNode : + if ( (indent == 0) || (! node.ns.empty()) || (! node.name.empty()) || + (! node.attrs.empty()) || (! node.children.empty()) ) fprintf ( log, " ** invalid cdata ** \n" ); + fprintf ( log, "\"" ); + PutClearString ( log, node.value ); + fprintf ( log, "\"\n" ); + break; + + case kPINode : + fprintf ( log, "PI %s", node.name.c_str() ); + if ( (indent == 0) || node.name.empty() || (! node.children.empty()) ) fprintf ( log, " ** invalid pi ** \n" ); + if ( ! node.value.empty() ) { + fprintf ( log, " " ); + } + fprintf ( log, "\n" ); + break; + + } + +} // DumpXMLTree + +#endif // DumpXMLParseTree + + +// ------------------------------------------------------------------------------------------------- +// CompareNodeNames +// ---------------- +// +// Comparison routine for sorting XMP nodes by name. The name "xml:lang" is less than anything else, +// and "rdf:type" is less than anything except "xml:lang". This preserves special rules for qualifiers. + +static bool +CompareNodeNames ( XMP_Node * left, XMP_Node * right ) +{ + + if ( left->name == "xml:lang" ) return true; + if ( right->name == "xml:lang" ) return false; + + if ( left->name == "rdf:type" ) return true; + if ( right->name == "rdf:type" ) return false; + + return ( left->name < right->name ); + +} // CompareNodeNames + + +// ------------------------------------------------------------------------------------------------- +// CompareNodeValues +// ----------------- +// +// Comparison routine for sorting XMP nodes by value. + +static bool +CompareNodeValues ( XMP_Node * left, XMP_Node * right ) +{ + + if ( XMP_PropIsSimple ( left->options ) && XMP_PropIsSimple ( right->options ) ) { + return ( left->value < right->value ); + } + + XMP_OptionBits leftForm = left->options & kXMP_PropCompositeMask; + XMP_OptionBits rightForm = right->options & kXMP_PropCompositeMask; + + return ( leftForm < rightForm ); + +} // CompareNodeValues + + +// ------------------------------------------------------------------------------------------------- +// CompareNodeLangs +// ---------------- +// +// Comparison routine for sorting XMP nodes by xml:lang qualifier. An "x-default" value is less than +// any other language. + +static bool +CompareNodeLangs ( XMP_Node * left, XMP_Node * right ) +{ + + if ( left->qualifiers.empty() || (left->qualifiers[0]->name != "xml:lang") ) return false; + if ( right->qualifiers.empty() || (right->qualifiers[0]->name != "xml:lang") ) return false; + + if ( left->qualifiers[0]->value == "x-default" ) return true; + if ( right->qualifiers[0]->value == "x-default" ) return false; + + return ( left->qualifiers[0]->value < right->qualifiers[0]->value ); + +} // CompareNodeLangs + + +// ------------------------------------------------------------------------------------------------- +// SortWithinOffspring +// ------------------- +// +// Sort one level down, within the elements of a node vector. This sorts the qualifiers of each +// node. If the node is a struct it sorts the fields by names. If the node is an unordered array it +// sorts the elements by value. If the node is an AltText array it sorts the elements by language. + +static void +SortWithinOffspring ( XMP_NodeOffspring & nodeVec ) +{ + + for ( size_t i = 0, limit = nodeVec.size(); i < limit; ++i ) { + + XMP_Node * currPos = nodeVec[i]; + + if ( ! currPos->qualifiers.empty() ) { + sort ( currPos->qualifiers.begin(), currPos->qualifiers.end(), CompareNodeNames ); + SortWithinOffspring ( currPos->qualifiers ); + } + + if ( ! currPos->children.empty() ) { + + if ( XMP_PropIsStruct ( currPos->options ) || XMP_NodeIsSchema ( currPos->options ) ) { + sort ( currPos->children.begin(), currPos->children.end(), CompareNodeNames ); + } else if ( XMP_PropIsArray ( currPos->options ) ) { + if ( XMP_ArrayIsUnordered ( currPos->options ) ) { + stable_sort ( currPos->children.begin(), currPos->children.end(), CompareNodeValues ); + } else if ( XMP_ArrayIsAltText ( currPos->options ) ) { + sort ( currPos->children.begin(), currPos->children.end(), CompareNodeLangs ); + } + } + + SortWithinOffspring ( currPos->children ); + + } + + } + +} // SortWithinOffspring + + +// ------------------------------------------------------------------------------------------------- +// RegisterAlias +// ------------- +// +// Allow 3 kinds of alias: +// TopProp => TopProp +// TopProp => TopArray[1] +// TopProp => TopArray[@xml:lang='x-default'] +// +// A new alias can be made to something that is already aliased, as long as the net result is one of +// the legitimate forms. The new alias can already have aliases to it, also as long as result of +// adjusting all of the exiting aliases leaves them legal. +// +// ! The caller assumes all risk that new aliases do not invalidate existing XMPMeta objects. Any +// ! conflicts will result in later references throwing bad XPath exceptions. + +static void +RegisterAlias ( XMP_StringPtr aliasNS, + XMP_StringPtr aliasProp, + XMP_StringPtr actualNS, + XMP_StringPtr actualProp, + XMP_OptionBits arrayForm ) +{ + XMP_ExpandedXPath expAlias, expActual; + XMP_AliasMapPos mapPos; + XMP_ExpandedXPath * regActual = 0; + + XMP_Assert ( (aliasNS != 0) && (aliasProp != 0) && (actualNS != 0) && (actualProp != 0) ); // Enforced by wrapper. + + // Expand the alias and actual names, make sure they are one of the basic 3 forms. When counting + // the expanded XPath size remember that the schema URI is the first component. We don't have to + // compare the schema URIs though, the (unique) prefix is part of the top property name. + + ExpandXPath ( aliasNS, aliasProp, &expAlias ); + ExpandXPath ( actualNS, actualProp, &expActual ); + if ( (expAlias.size() != 2) || (expActual.size() != 2) ) { + XMP_Throw ( "Alias and actual property names must be simple", kXMPErr_BadXPath ); + } + + arrayForm = VerifySetOptions ( arrayForm, 0 ); + if ( arrayForm != 0 ) { + if ( (arrayForm & ~kXMP_PropArrayFormMask) != 0 ) XMP_Throw ( "Only array form flags are allowed", kXMPErr_BadOptions ); + expActual[1].options |= arrayForm; // Set the array form for the top level step. + if ( ! (arrayForm & kXMP_PropArrayIsAltText) ) { + expActual.push_back ( XPathStepInfo ( "[1]", kXMP_ArrayIndexStep ) ); + } else { + expActual.push_back ( XPathStepInfo ( "[?xml:lang=\"x-default\"]", kXMP_QualSelectorStep ) ); + } + } + + // See if there are any conflicts with existing aliases. A couple of the checks are easy. If the + // alias is already aliased it is only OK to reregister an identical alias. If the actual is + // already aliased to something else and the new chain is legal, just swap in the old base. + + mapPos = sRegisteredAliasMap->find ( expAlias[kRootPropStep].step ); + if ( mapPos != sRegisteredAliasMap->end() ) { + + // This alias is already registered to something, make sure it is the same something. + + regActual = &mapPos->second; + if ( arrayForm != (mapPos->second[1].options & kXMP_PropArrayFormMask) ) { + XMP_Throw ( "Mismatch with existing alias array form", kXMPErr_BadParam ); + } + if ( expActual.size() != regActual->size() ) { + XMP_Throw ( "Mismatch with existing actual path", kXMPErr_BadParam ); + } + if ( expActual[kRootPropStep].step != (*regActual)[kRootPropStep].step ) { + XMP_Throw ( "Mismatch with existing actual name", kXMPErr_BadParam ); + } + if ( (expActual.size() == 3) && (expActual[kAliasIndexStep].step != (*regActual)[kAliasIndexStep].step) ) { + XMP_Throw ( "Mismatch with existing actual array item", kXMPErr_BadParam ); + } + return; + + } + + mapPos = sRegisteredAliasMap->find ( expActual[kRootPropStep].step ); + if ( mapPos != sRegisteredAliasMap->end() ) { + + // The actual is already aliased to something else. + + regActual = &mapPos->second; + if ( expActual.size() == 2 ) { + expActual = *regActual; // TopProp => TopProp => anything : substitute the entire old base. + } else if ( regActual->size() != 2 ) { + XMP_Throw ( "Can't alias an array item to an array item", kXMPErr_BadParam ); // TopProp => TopArray[] => TopArray[] : nope. + } else { + expActual[kSchemaStep].step = (*regActual)[kSchemaStep].step; // TopProp => TopArray[] => TopProp : + expActual[kRootPropStep].step = (*regActual)[kRootPropStep].step; // substitute the old base name. + } + + } + + // Checking for existing aliases to this one is touchier. This involves updating the alias map, + // which must not be done unless all of the changes are legal. So we need 2 loops, one to verify + // that everything is OK, and one to make the changes. The bad case is: + // TopProp => TopArray[] => TopArray[] + // In the valid cases we back substitute the new base. + + for ( mapPos = sRegisteredAliasMap->begin(); mapPos != sRegisteredAliasMap->end(); ++mapPos ) { + regActual = &mapPos->second; + if ( expAlias[kRootPropStep].step == (*regActual)[kRootPropStep].step ) { + if ( (regActual->size() == 2) && (expAlias.size() == 2) ) { + XMP_Throw ( "Can't alias an array item to an array item", kXMPErr_BadParam ); + } + } + } + + for ( mapPos = sRegisteredAliasMap->begin(); mapPos != sRegisteredAliasMap->end(); ++mapPos ) { + regActual = &mapPos->second; + if ( expAlias[kRootPropStep].step == (*regActual)[kRootPropStep].step ) { + + if ( regActual->size() == 1 ) { + *regActual = expActual; // TopProp => TopProp => anything : substitute the entire new base. + } else { + (*regActual)[kSchemaStep].step = expActual[kSchemaStep].step; // TopProp => TopArray[] => TopProp : + (*regActual)[kRootPropStep].step = expActual[kRootPropStep].step; // substitute the new base name. + } + + } + } + + // Finally, all is OK to register the new alias. + + (void) sRegisteredAliasMap->insert ( XMP_AliasMap::value_type ( expAlias[kRootPropStep].step, expActual ) ); + +} // RegisterAlias + + +// ------------------------------------------------------------------------------------------------- +// RegisterStandardAliases +// ----------------------- + +static void +RegisterStandardAliases() +{ + + // Aliases from XMP to DC. + RegisterAlias ( kXMP_NS_XMP, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered ); + RegisterAlias ( kXMP_NS_XMP, "Authors", kXMP_NS_DC, "creator", 0 ); + RegisterAlias ( kXMP_NS_XMP, "Description", kXMP_NS_DC, "description", 0 ); + RegisterAlias ( kXMP_NS_XMP, "Format", kXMP_NS_DC, "format", 0 ); + RegisterAlias ( kXMP_NS_XMP, "Keywords", kXMP_NS_DC, "subject", 0 ); + RegisterAlias ( kXMP_NS_XMP, "Locale", kXMP_NS_DC, "language", 0 ); + RegisterAlias ( kXMP_NS_XMP, "Title", kXMP_NS_DC, "title", 0 ); + RegisterAlias ( kXMP_NS_XMP_Rights, "Copyright", kXMP_NS_DC, "rights", 0 ); + + // Aliases from PDF to DC and XMP. + RegisterAlias ( kXMP_NS_PDF, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered ); + RegisterAlias ( kXMP_NS_PDF, "BaseURL", kXMP_NS_XMP, "BaseURL", 0 ); + RegisterAlias ( kXMP_NS_PDF, "CreationDate", kXMP_NS_XMP, "CreateDate", 0 ); + RegisterAlias ( kXMP_NS_PDF, "Creator", kXMP_NS_XMP, "CreatorTool", 0 ); + RegisterAlias ( kXMP_NS_PDF, "ModDate", kXMP_NS_XMP, "ModifyDate", 0 ); + RegisterAlias ( kXMP_NS_PDF, "Subject", kXMP_NS_DC, "description", kXMP_PropArrayIsAltText ); + RegisterAlias ( kXMP_NS_PDF, "Title", kXMP_NS_DC, "title", kXMP_PropArrayIsAltText ); + + // Aliases from Photoshop to DC and XMP. + RegisterAlias ( kXMP_NS_Photoshop, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered ); + RegisterAlias ( kXMP_NS_Photoshop, "Caption", kXMP_NS_DC, "description", kXMP_PropArrayIsAltText ); + RegisterAlias ( kXMP_NS_Photoshop, "Copyright", kXMP_NS_DC, "rights", kXMP_PropArrayIsAltText ); + RegisterAlias ( kXMP_NS_Photoshop, "Keywords", kXMP_NS_DC, "subject", 0 ); + RegisterAlias ( kXMP_NS_Photoshop, "Marked", kXMP_NS_XMP_Rights, "Marked", 0 ); + RegisterAlias ( kXMP_NS_Photoshop, "Title", kXMP_NS_DC, "title", kXMP_PropArrayIsAltText ); + RegisterAlias ( kXMP_NS_Photoshop, "WebStatement", kXMP_NS_XMP_Rights, "WebStatement", 0 ); + + // Aliases from TIFF and EXIF to DC and XMP. + RegisterAlias ( kXMP_NS_TIFF, "Artist", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered); + RegisterAlias ( kXMP_NS_TIFF, "Copyright", kXMP_NS_DC, "rights", 0 ); + RegisterAlias ( kXMP_NS_TIFF, "DateTime", kXMP_NS_XMP, "ModifyDate", 0 ); + RegisterAlias ( kXMP_NS_EXIF, "DateTimeDigitized", kXMP_NS_XMP, "CreateDate", 0 ); + RegisterAlias ( kXMP_NS_TIFF, "ImageDescription", kXMP_NS_DC, "description", 0 ); + RegisterAlias ( kXMP_NS_TIFF, "Software", kXMP_NS_XMP, "CreatorTool", 0 ); + + // Aliases from PNG to DC and XMP. + RegisterAlias ( kXMP_NS_PNG, "Author", kXMP_NS_DC, "creator", kXMP_PropArrayIsOrdered); + RegisterAlias ( kXMP_NS_PNG, "Copyright", kXMP_NS_DC, "rights", kXMP_PropArrayIsAltText); + RegisterAlias ( kXMP_NS_PNG, "CreationTime", kXMP_NS_XMP, "CreateDate", 0 ); + RegisterAlias ( kXMP_NS_PNG, "Description", kXMP_NS_DC, "description", kXMP_PropArrayIsAltText); + RegisterAlias ( kXMP_NS_PNG, "ModificationTime", kXMP_NS_XMP, "ModifyDate", 0 ); + RegisterAlias ( kXMP_NS_PNG, "Software", kXMP_NS_XMP, "CreatorTool", 0 ); + RegisterAlias ( kXMP_NS_PNG, "Title", kXMP_NS_DC, "title", kXMP_PropArrayIsAltText); + +} // RegisterStandardAliases + + +// ================================================================================================= +// Constructors +// ============ + + +XMPMeta::XMPMeta() : tree(XMP_Node(0,"",0)), clientRefs(0), xmlParser(0) +{ + #if XMP_TraceCTorDTor + printf ( "Default construct XMPMeta @ %.8X\n", this ); + #endif + + if ( sDefaultErrorCallback.clientProc != 0 ) { + this->errorCallback.wrapperProc = sDefaultErrorCallback.wrapperProc; + this->errorCallback.clientProc = sDefaultErrorCallback.clientProc; + this->errorCallback.context = sDefaultErrorCallback.context; + this->errorCallback.limit = sDefaultErrorCallback.limit; + } + +} // XMPMeta + +// ------------------------------------------------------------------------------------------------- + +XMPMeta::~XMPMeta() RELEASE_NO_THROW +{ + #if XMP_TraceCTorDTor + printf ( "Destruct XMPMeta @ %.8X\n", this ); + #endif + + XMP_Assert ( this->clientRefs <= 0 ); + if ( xmlParser != 0 ) delete ( xmlParser ); + xmlParser = 0; + +} // ~XMPMeta + + +// ================================================================================================= +// Class Static Functions +// ====================== +// +// +// ================================================================================================= + +// ------------------------------------------------------------------------------------------------- +// GetVersionInfo +// -------------- + +/* class-static */ void +XMPMeta::GetVersionInfo ( XMP_VersionInfo * info ) +{ + + memset ( info, 0, sizeof(*info) ); // AUDIT: Safe, using sizeof the destination. + XMP_Assert ( sizeof(*info) == sizeof(XMP_VersionInfo) ); + + info->major = XMPCORE_API_VERSION_MAJOR; + info->minor = XMPCORE_API_VERSION_MINOR; + info->micro = 0; //no longer used + info->isDebug = kXMPCore_DebugFlag; + info->flags = 0; // ! None defined yet. + info->message = kXMPCore_VersionMessage; + +} // GetVersionInfo + +// ------------------------------------------------------------------------------------------------- +// Initialize +// ---------- + +#if XMP_TraceCoreCalls + FILE * xmpCoreLog = stderr; +#endif + +#if UseGlobalLibraryLock + XMP_BasicMutex sLibraryLock; +#endif + +/* class-static */ bool +XMPMeta::Initialize() +{ + // Allocate and initialize static objects. + + ++sXMP_InitCount; + if ( sXMP_InitCount > 1 ) return true; + + #if XMP_TraceCoreCallsToFile + xmpCoreLog = fopen ( "XMPCoreLog.txt", "w" ); + if ( xmpCoreLog == 0 ) xmpCoreLog = stderr; + #endif + + #if UseGlobalLibraryLock + InitializeBasicMutex ( sLibraryLock ); + #endif + + if ( ! Initialize_LibUtils() ) return false; + xdefaultName = new XMP_VarString ( "x-default" ); + + sRegisteredNamespaces = new XMP_NamespaceTable; + sRegisteredAliasMap = new XMP_AliasMap; + + InitializeUnicodeConversions(); + + + // Register standard namespaces and aliases. + + XMP_StringPtr voidPtr; + XMP_StringLen voidLen; + + (void) RegisterNamespace ( kXMP_NS_XML, "xml", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_RDF, "rdf", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_DC, "dc", &voidPtr, &voidLen ); + + (void) RegisterNamespace ( kXMP_NS_XMP, "xmp", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_PDF, "pdf", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_Photoshop, "photoshop", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_PSAlbum, "album", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_EXIF, "exif", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_EXIF_Aux, "aux", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_ExifEX, "exifEX", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_TIFF, "tiff", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_PNG, "png", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_JPEG, "jpeg", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_JP2K, "jp2k", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_CameraRaw, "crs", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_ASF, "asf", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_WAV, "wav", &voidPtr, &voidLen ); + + (void) RegisterNamespace ( kXMP_NS_AdobeStockPhoto, "bmsp", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_CreatorAtom, "creatorAtom", &voidPtr, &voidLen ); + + (void) RegisterNamespace ( kXMP_NS_XMP_Rights, "xmpRights", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_MM, "xmpMM", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_BJ, "xmpBJ", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_Note, "xmpNote", &voidPtr, &voidLen ); + + (void) RegisterNamespace ( kXMP_NS_DM, "xmpDM", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_Script, "xmpScript", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_BWF, "bext", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_AEScart, "AEScart", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_RIFFINFO, "riffinfo", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_Text, "xmpT", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_PagedFile, "xmpTPg", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_Graphics, "xmpG", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_Image, "xmpGImg", &voidPtr, &voidLen ); + + (void) RegisterNamespace ( kXMP_NS_XMP_Font, "stFnt", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_Dimensions, "stDim", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_ResourceEvent, "stEvt", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_ResourceRef, "stRef", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_ST_Version, "stVer", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_ST_Job, "stJob", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_XMP_ManifestItem, "stMfs", &voidPtr, &voidLen ); + + (void) RegisterNamespace ( kXMP_NS_XMP_IdentifierQual, "xmpidq", &voidPtr, &voidLen ); + + (void) RegisterNamespace ( kXMP_NS_IPTCCore, "Iptc4xmpCore", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_IPTCExt, "Iptc4xmpExt", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_DICOM, "DICOM", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_PLUS, "plus", &voidPtr, &voidLen ); + + (void) RegisterNamespace ( kXMP_NS_PDFA_Schema, "pdfaSchema", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_PDFA_Property, "pdfaProperty", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_PDFA_Type, "pdfaType", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_PDFA_Field, "pdfaField", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_PDFA_ID, "pdfaid", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_PDFA_Extension, "pdfaExtension", &voidPtr, &voidLen ); + + (void) RegisterNamespace ( kXMP_NS_PDFX, "pdfx", &voidPtr, &voidLen ); + (void) RegisterNamespace ( kXMP_NS_PDFX_ID, "pdfxid", &voidPtr, &voidLen ); + + (void) RegisterNamespace ( "adobe:ns:meta/", "x", &voidPtr, &voidLen ); + (void) RegisterNamespace ( "http://ns.adobe.com/iX/1.0/", "iX", &voidPtr, &voidLen ); + + RegisterStandardAliases(); + + // Initialize the other core classes. + + if ( ! XMPIterator::Initialize() ) XMP_Throw ( "Failure from XMPIterator::Initialize", kXMPErr_InternalFailure ); + if ( ! XMPUtils::Initialize() ) XMP_Throw ( "Failure from XMPUtils::Initialize", kXMPErr_InternalFailure ); + // Do miscelaneous semantic checks of types and arithmetic. + + XMP_Assert ( sizeof(XMP_Int8) == 1 ); + XMP_Assert ( sizeof(XMP_Int16) == 2 ); + XMP_Assert ( sizeof(XMP_Int32) == 4 ); + XMP_Assert ( sizeof(XMP_Int64) == 8 ); + XMP_Assert ( sizeof(XMP_Uns8) == 1 ); + XMP_Assert ( sizeof(XMP_Uns16) == 2 ); + XMP_Assert ( sizeof(XMP_Uns32) == 4 ); + XMP_Assert ( sizeof(XMP_Uns64) == 8 ); + XMP_Assert ( sizeof(XMP_Bool) == 1 ); + + XMP_Assert ( sizeof(XMP_OptionBits) == 4 ); // Check that option masking work on all 32 bits. + XMP_OptionBits flag = (XMP_OptionBits) (~0UL); + XMP_Assert ( flag == (XMP_OptionBits)(-1L) ); + XMP_Assert ( (flag ^ kXMP_PropHasLang) == 0xFFFFFFBFUL ); + XMP_Assert ( (flag & ~kXMP_PropHasLang) == 0xFFFFFFBFUL ); + + XMP_OptionBits opt1 = 0; // Check the general option bit macros. + XMP_OptionBits opt2 = (XMP_OptionBits)~0UL; + XMP_SetOption ( opt1, kXMP_PropValueIsArray ); + XMP_ClearOption ( opt2, kXMP_PropValueIsArray ); + XMP_Assert ( opt1 == ~opt2 ); + XMP_Assert ( XMP_TestOption ( opt1, kXMP_PropValueIsArray ) ); + XMP_Assert ( ! XMP_TestOption ( opt2, kXMP_PropValueIsArray ) ); + + XMP_Assert ( XMP_PropIsSimple ( ~kXMP_PropCompositeMask ) ); // Check the special option bit macros. + XMP_Assert ( ! XMP_PropIsSimple ( kXMP_PropValueIsStruct ) ); + XMP_Assert ( ! XMP_PropIsSimple ( kXMP_PropValueIsArray ) ); + + XMP_Assert ( XMP_PropIsStruct ( kXMP_PropValueIsStruct ) ); + XMP_Assert ( XMP_PropIsArray ( kXMP_PropValueIsArray ) ); + XMP_Assert ( ! XMP_PropIsStruct ( ~kXMP_PropValueIsStruct ) ); + XMP_Assert ( ! XMP_PropIsArray ( ~kXMP_PropValueIsArray ) ); + + XMP_Assert ( XMP_ArrayIsUnordered ( ~kXMP_PropArrayIsOrdered ) ); + XMP_Assert ( XMP_ArrayIsOrdered ( kXMP_PropArrayIsOrdered ) ); + XMP_Assert ( XMP_ArrayIsAlternate ( kXMP_PropArrayIsAlternate ) ); + XMP_Assert ( XMP_ArrayIsAltText ( kXMP_PropArrayIsAltText ) ); + XMP_Assert ( ! XMP_ArrayIsUnordered ( kXMP_PropArrayIsOrdered ) ); + XMP_Assert ( ! XMP_ArrayIsOrdered ( ~kXMP_PropArrayIsOrdered ) ); + XMP_Assert ( ! XMP_ArrayIsAlternate ( ~kXMP_PropArrayIsAlternate ) ); + XMP_Assert ( ! XMP_ArrayIsAltText ( ~kXMP_PropArrayIsAltText ) ); + + XMP_Assert ( XMP_PropHasQualifiers ( kXMP_PropHasQualifiers ) ); + XMP_Assert ( XMP_PropIsQualifier ( kXMP_PropIsQualifier ) ); + XMP_Assert ( XMP_PropHasLang ( kXMP_PropHasLang ) ); + XMP_Assert ( ! XMP_PropHasQualifiers ( ~kXMP_PropHasQualifiers ) ); + XMP_Assert ( ! XMP_PropIsQualifier ( ~kXMP_PropIsQualifier ) ); + XMP_Assert ( ! XMP_PropHasLang ( ~kXMP_PropHasLang ) ); + + XMP_Assert ( XMP_NodeIsSchema ( kXMP_SchemaNode ) ); + XMP_Assert ( XMP_PropIsAlias ( kXMP_PropIsAlias ) ); + XMP_Assert ( ! XMP_NodeIsSchema ( ~kXMP_SchemaNode ) ); + XMP_Assert ( ! XMP_PropIsAlias ( ~kXMP_PropIsAlias ) ); + + #if 0 // Generally off, enable to hand check generated code. + extern XMP_OptionBits opt3, opt4; + if ( XMP_TestOption ( opt3, kXMP_PropValueIsArray ) ) opt4 = opt3; + if ( ! XMP_TestOption ( opt3, kXMP_PropValueIsStruct ) ) opt4 = opt3; + static bool ok1 = XMP_TestOption ( opt4, kXMP_PropValueIsArray ); + static bool ok2 = ! XMP_TestOption ( opt4, kXMP_PropValueIsStruct ); + #endif + + // Make sure the embedded info strings are referenced and kept. + if ( (kXMPCore_EmbeddedVersion[0] == 0) || (kXMPCore_EmbeddedCopyright[0] == 0) ) return false; + return true; + +} // Initialize + + +// ------------------------------------------------------------------------------------------------- +// Terminate +// --------- + +/* class-static */ void +XMPMeta::Terminate() RELEASE_NO_THROW +{ + --sXMP_InitCount; + if ( sXMP_InitCount != 0 ) return; // Not ready to terminate, or already terminated. + + XMPIterator::Terminate(); + XMPUtils::Terminate(); +#if ENABLE_NEW_DOM_MODEL + NS_XMPCOMMON::ITSingleton< NS_INT_XMPCORE::IXMPCoreObjectFactory >::DestroyInstance(); + NS_INT_XMPCOMMON::TerminateXMPCommonFramework(); +#endif + + EliminateGlobal ( sRegisteredNamespaces ); + EliminateGlobal ( sRegisteredAliasMap ); + + EliminateGlobal ( xdefaultName ); + + Terminate_LibUtils(); + + #if UseGlobalLibraryLock + TerminateBasicMutex ( sLibraryLock ); + #endif + + #if XMP_TraceCoreCallsToFile + if ( xmpCoreLog != stderr ) fclose ( xmpCoreLog ); + xmpCoreLog = stderr; + #endif + + // reset static variables + sDefaultErrorCallback.Clear(); +} // Terminate + + +// ------------------------------------------------------------------------------------------------- +// DumpNamespaces +// -------------- +// +// Dump the prefix to URI map (easier to read) and verify that both are consistent and legit. + +// *** Should put checks in a separate routine for regular calling in debug builds. + +/* class-static */ XMP_Status +XMPMeta::DumpNamespaces ( XMP_TextOutputProc outProc, + void * refCon ) +{ + + sRegisteredNamespaces->Dump ( outProc, refCon ); + return 0; + +} // DumpNamespaces + + +// ------------------------------------------------------------------------------------------------- +// GetGlobalOptions +// ---------------- + +/* class-static */ XMP_OptionBits +XMPMeta::GetGlobalOptions() +{ + XMP_OptionBits options = 0; + + return options; + +} // GetGlobalOptions + + +// ------------------------------------------------------------------------------------------------- +// SetGlobalOptions +// ---------------- + +/* class-static */ void +XMPMeta::SetGlobalOptions ( XMP_OptionBits options ) +{ + + XMP_Throw ( "Unimplemented method XMPMeta::SetGlobalOptions", kXMPErr_Unimplemented ); + void * p; p = &options; // Avoid unused param warnings. + +} // SetGlobalOptions + + +// ------------------------------------------------------------------------------------------------- +// RegisterNamespace +// ----------------- + +/* class-static */ bool +XMPMeta::RegisterNamespace ( XMP_StringPtr namespaceURI, + XMP_StringPtr suggestedPrefix, + XMP_StringPtr * registeredPrefix, + XMP_StringLen * prefixSize ) +{ + + return sRegisteredNamespaces->Define ( namespaceURI, suggestedPrefix, registeredPrefix, prefixSize ); + +} // RegisterNamespace + + +// ------------------------------------------------------------------------------------------------- +// GetNamespacePrefix +// ------------------ + +/* class-static */ bool +XMPMeta::GetNamespacePrefix ( XMP_StringPtr namespaceURI, + XMP_StringPtr * namespacePrefix, + XMP_StringLen * prefixSize ) +{ + + return sRegisteredNamespaces->GetPrefix ( namespaceURI, namespacePrefix, prefixSize ); + +} // GetNamespacePrefix + + +// ------------------------------------------------------------------------------------------------- +// GetNamespaceURI +// --------------- + +/* class-static */ bool +XMPMeta::GetNamespaceURI ( XMP_StringPtr namespacePrefix, + XMP_StringPtr * namespaceURI, + XMP_StringLen * uriSize ) +{ + + return sRegisteredNamespaces->GetURI ( namespacePrefix, namespaceURI, uriSize ); + +} // GetNamespaceURI + + +// ------------------------------------------------------------------------------------------------- +// DeleteNamespace +// --------------- + +// *** Don't allow standard namespaces to be deleted. +// *** We would be better off not having this. Instead, have local namespaces from parsing be +// *** restricted to the object that introduced them. + +/* class-static */ void +XMPMeta::DeleteNamespace ( XMP_StringPtr namespaceURI ) +{ + + XMP_Throw ( "Unimplemented method XMPMeta::DeleteNamespace", kXMPErr_Unimplemented ); + +} // DeleteNamespace + + +// ================================================================================================= +// Class Methods +// ============= +// +// +// ================================================================================================= + + +// ------------------------------------------------------------------------------------------------- +// DumpObject +// ---------- + +void +XMPMeta::DumpObject ( XMP_TextOutputProc outProc, + void * refCon ) const +{ + XMP_Assert ( outProc != 0 ); // ! Enforced by wrapper. + + OutProcLiteral ( "Dumping XMPMeta object \"" ); + DumpClearString ( tree.name, outProc, refCon ); + OutProcNChars ( "\" ", 3 ); + DumpNodeOptions ( tree.options, outProc, refCon ); + #if 0 // *** XMP_DebugBuild + if ( (tree._namePtr != tree.name.c_str()) || + (tree._valuePtr != tree.value.c_str()) ) OutProcLiteral ( " ** bad debug string **" ); + #endif + OutProcNewline(); + + if ( ! tree.value.empty() ) { + OutProcLiteral ( "** bad root value ** \"" ); + DumpClearString ( tree.value, outProc, refCon ); + OutProcNChars ( "\"", 1 ); + OutProcNewline(); + } + + if ( ! tree.qualifiers.empty() ) { + OutProcLiteral ( "** bad root qualifiers **" ); + OutProcNewline(); + for ( size_t qualNum = 0, qualLim = tree.qualifiers.size(); qualNum < qualLim; ++qualNum ) { + DumpPropertyTree ( tree.qualifiers[qualNum], 3, 0, outProc, refCon ); + } + } + + if ( ! tree.children.empty() ) { + + for ( size_t childNum = 0, childLim = tree.children.size(); childNum < childLim; ++childNum ) { + + const XMP_Node * currSchema = tree.children[childNum]; + + OutProcNewline(); + OutProcIndent ( 1 ); + DumpClearString ( currSchema->value, outProc, refCon ); + OutProcNChars ( " ", 2 ); + DumpClearString ( currSchema->name, outProc, refCon ); + OutProcNChars ( " ", 2 ); + DumpNodeOptions ( currSchema->options, outProc, refCon ); + #if 0 // *** XMP_DebugBuild + if ( (currSchema->_namePtr != currSchema->name.c_str()) || + (currSchema->_valuePtr != currSchema->value.c_str()) ) OutProcLiteral ( " ** bad debug string **" ); + #endif + OutProcNewline(); + + if ( ! (currSchema->options & kXMP_SchemaNode) ) { + OutProcLiteral ( "** bad schema options **" ); + OutProcNewline(); + } + + if ( ! currSchema->qualifiers.empty() ) { + OutProcLiteral ( "** bad schema qualifiers **" ); + OutProcNewline(); + for ( size_t qualNum = 0, qualLim = currSchema->qualifiers.size(); qualNum < qualLim; ++qualNum ) { + DumpPropertyTree ( currSchema->qualifiers[qualNum], 3, 0, outProc, refCon ); + } + } + + for ( size_t numChild = 0, childLimit = currSchema->children.size(); numChild < childLimit; ++numChild ) { + DumpPropertyTree ( currSchema->children[numChild], 2, 0, outProc, refCon ); + } + + } + + } + +} // DumpObject + + +// ------------------------------------------------------------------------------------------------- +// CountArrayItems +// --------------- + +XMP_Index +XMPMeta::CountArrayItems ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName ) const +{ + XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // Enforced by wrapper. + + XMP_ExpandedXPath expPath; + ExpandXPath ( schemaNS, arrayName, &expPath ); + + const XMP_Node * arrayNode = FindConstNode ( &tree, expPath ); + + if ( arrayNode == 0 ) return 0; + if ( ! (arrayNode->options & kXMP_PropValueIsArray) ) XMP_Throw ( "The named property is not an array", kXMPErr_BadXPath ); + return arrayNode->children.size(); + +} // CountArrayItems + + +// ------------------------------------------------------------------------------------------------- +// GetObjectName +// ------------- + +void +XMPMeta::GetObjectName ( XMP_StringPtr * namePtr, + XMP_StringLen * nameLen ) const +{ + + *namePtr = tree.name.c_str(); + *nameLen = tree.name.size(); + +} // GetObjectName + + +// ------------------------------------------------------------------------------------------------- +// SetObjectName +// ------------- + +void +XMPMeta::SetObjectName ( XMP_StringPtr name ) +{ + VerifyUTF8 ( name ); // Throws if the string is not legit UTF-8. + tree.name = name; + +} // SetObjectName + + +// ------------------------------------------------------------------------------------------------- +// GetObjectOptions +// ---------------- + +XMP_OptionBits +XMPMeta::GetObjectOptions() const +{ + XMP_OptionBits options = 0; + + return options; + +} // GetObjectOptions + + +// ------------------------------------------------------------------------------------------------- +// SetObjectOptions +// ---------------- + +void +XMPMeta::SetObjectOptions ( XMP_OptionBits options ) +{ + + XMP_Throw ( "Unimplemented method XMPMeta::SetObjectOptions", kXMPErr_Unimplemented ); + void * p; p = &options; // Avoid unused param warnings. + +} // SetObjectOptions + + +// ------------------------------------------------------------------------------------------------- +// Sort +// ---- +// +// At the top level the namespaces are sorted by their prefixes. Within a namespace, the top level +// properties are sorted by name. Within a struct, the fields are sorted by their qualified name, +// i.e. their XML prefix:local form. Unordered arrays of simple items are sorted by value. Language +// Alternative arrays are sorted by the xml:lang qualifiers, with the "x-default" item placed first. + +void +XMPMeta::Sort() +{ + + if ( ! this->tree.qualifiers.empty() ) { + sort ( this->tree.qualifiers.begin(), this->tree.qualifiers.end(), CompareNodeNames ); + SortWithinOffspring ( this->tree.qualifiers ); + } + + if ( ! this->tree.children.empty() ) { + // The schema prefixes are the node's value, the name is the URI, so we sort schemas by value. + sort ( this->tree.children.begin(), this->tree.children.end(), CompareNodeValues ); + SortWithinOffspring ( this->tree.children ); + } + +} // Sort + + +// ------------------------------------------------------------------------------------------------- +// Erase +// ----- +// +// Clear everything except for clientRefs. + +void +XMPMeta::Erase() +{ + + if ( this->xmlParser != 0 ) { + delete ( this->xmlParser ); + this->xmlParser = 0; + } + this->tree.ClearNode(); + +} // Erase + + +// ------------------------------------------------------------------------------------------------- +// Clone +// ----- + +void +XMPMeta::Clone ( XMPMeta * clone, XMP_OptionBits options ) const +{ + if ( clone == 0 ) XMP_Throw ( "Null clone pointer", kXMPErr_BadParam ); + if ( options != 0 ) XMP_Throw ( "No options are defined yet", kXMPErr_BadOptions ); + XMP_Assert ( this->tree.parent == 0 ); + + clone->tree.ClearNode(); + + clone->tree.options = this->tree.options; + clone->tree.name = this->tree.name; + clone->tree.value = this->tree.value; + clone->errorCallback = this->errorCallback; + + #if 0 // *** XMP_DebugBuild + clone->tree._namePtr = clone->tree.name.c_str(); + clone->tree._valuePtr = clone->tree.value.c_str(); + #endif + + CloneOffspring ( &this->tree, &clone->tree ); + +} // Clone + +// ================================================================================================= +// XMP_Node::GetLocalURI +// ===================== +// +// This has to be someplace where XMPMeta::GetNamespaceURI is visible. + +void XMP_Node::GetLocalURI ( XMP_StringPtr * uriStr, XMP_StringLen * uriSize ) const +{ + + if ( uriStr != 0 ) *uriStr = ""; // Set up empty defaults. + if ( uriSize != 0 ) *uriSize = 0; + + if ( this->name.empty() ) return; + + if ( XMP_NodeIsSchema ( this->options ) ) { + + if ( uriStr != 0 ) *uriStr = this->name.c_str(); + if ( uriSize != 0 ) *uriSize = this->name.size(); + + } else { + + size_t colonPos = this->name.find_first_of(':'); + if ( colonPos == XMP_VarString::npos ) return; // ! Name of array items is "[]". + + XMP_VarString prefix ( this->name, 0, colonPos ); + XMPMeta::GetNamespaceURI ( prefix.c_str(), uriStr, uriSize ); + + } + +} + +// ================================================================================================= +// Error notifications +// =================== + +// ------------------------------------------------------------------------------------------------- +// SetDefaultErrorCallback +// ----------------------- + +/* class-static */ void +XMPMeta::SetDefaultErrorCallback ( XMPMeta_ErrorCallbackWrapper wrapperProc, + XMPMeta_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit ) +{ + XMP_Assert ( wrapperProc != 0 ); // Must always be set by the glue; + + sDefaultErrorCallback.wrapperProc = wrapperProc; + sDefaultErrorCallback.clientProc = clientProc; + sDefaultErrorCallback.context = context; + sDefaultErrorCallback.limit = limit; + +} // SetDefaultErrorCallback + +// ------------------------------------------------------------------------------------------------- +// SetErrorCallback +// ---------------- + +void +XMPMeta::SetErrorCallback ( XMPMeta_ErrorCallbackWrapper wrapperProc, + XMPMeta_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit ) +{ + XMP_Assert ( wrapperProc != 0 ); // Must always be set by the glue; + + this->errorCallback.Clear(); + this->errorCallback.wrapperProc = wrapperProc; + this->errorCallback.clientProc = clientProc; + this->errorCallback.context = context; + this->errorCallback.limit = limit; + +} // SetErrorCallback + +// ------------------------------------------------------------------------------------------------- +// ResetErrorCallbackLimit +// ----------------------- + +void +XMPMeta::ResetErrorCallbackLimit ( XMP_Uns32 limit ) +{ + + this->errorCallback.limit = limit; + this->errorCallback.notifications = 0; + this->errorCallback.topSeverity = kXMPErrSev_Recoverable; + +} // ResetErrorCallbackLimit + +// ------------------------------------------------------------------------------------------------- +// ErrorCallbackInfo::CanNotify +// ------------------------------- +// +// This is const just to be usable from const XMPMeta functions. + +bool XMPMeta::ErrorCallbackInfo::CanNotify() const +{ + XMP_Assert ( (this->clientProc == 0) || (this->wrapperProc != 0) ); + return ( this->clientProc != 0); +} + +// ------------------------------------------------------------------------------------------------- +// ErrorCallbackInfo::ClientCallbackWrapper +// ------------------------------- +// +// This is const just to be usable from const XMPMeta functions. + +bool XMPMeta::ErrorCallbackInfo::ClientCallbackWrapper ( XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr messsage ) const +{ + XMP_Bool retValue = (*this->wrapperProc) ( this->clientProc, this->context, severity, cause, messsage ); + return ConvertXMP_BoolToBool(retValue); +} + +// ================================================================================================= diff --git a/source/lib/xmp_core/XMPMeta.hpp b/source/lib/xmp_core/XMPMeta.hpp new file mode 100644 index 0000000..2542f79 --- /dev/null +++ b/source/lib/xmp_core/XMPMeta.hpp @@ -0,0 +1,428 @@ +#ifndef __XMPMeta_hpp__ +#define __XMPMeta_hpp__ + +// ================================================================================================= +// Copyright 2003 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" +#include "public/include/XMP_Const.h" +#include "XMPCore_Impl.hpp" +#include "XMLParserAdapter.hpp" + +// ------------------------------------------------------------------------------------------------- + +#ifndef DumpXMLParseTree + #define DumpXMLParseTree 0 +#endif + +extern XMP_VarString * xdefaultName; // Needed in XMPMeta-Parse.cpp, MoveExplicitAliases. + +class XMPIterator; +class XMPUtils; + +// ------------------------------------------------------------------------------------------------- + +class XMPMeta { +public: + + static void + GetVersionInfo ( XMP_VersionInfo * info ); + + static bool + Initialize(); + static void + Terminate() RELEASE_NO_THROW; + + // --------------------------------------------------------------------------------------------- + + XMPMeta(); + + virtual ~XMPMeta() RELEASE_NO_THROW; + + // --------------------------------------------------------------------------------------------- + + static XMP_OptionBits + GetGlobalOptions(); + + static void + SetGlobalOptions ( XMP_OptionBits options ); + + // --------------------------------------------------------------------------------------------- + + static XMP_Status + DumpNamespaces ( XMP_TextOutputProc outProc, + void * refCon ); + + // --------------------------------------------------------------------------------------------- + + static bool + RegisterNamespace ( XMP_StringPtr namespaceURI, + XMP_StringPtr suggestedPrefix, + XMP_StringPtr * registeredPrefix, + XMP_StringLen * prefixSize ); + + static bool + GetNamespacePrefix ( XMP_StringPtr namespaceURI, + XMP_StringPtr * namespacePrefix, + XMP_StringLen * prefixSize ); + + static bool + GetNamespaceURI ( XMP_StringPtr namespacePrefix, + XMP_StringPtr * namespaceURI, + XMP_StringLen * uriSize ); + + static void + DeleteNamespace ( XMP_StringPtr namespaceURI ); + + // --------------------------------------------------------------------------------------------- + + bool + GetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr * propValue, + XMP_StringLen * valueSize, + XMP_OptionBits * options ) const; + + bool + GetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_StringPtr * itemValue, + XMP_StringLen * valueSize, + XMP_OptionBits * options ) const; + + bool + GetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr * fieldValue, + XMP_StringLen * valueSize, + XMP_OptionBits * options ) const; + + bool + GetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_StringPtr * qualValue, + XMP_StringLen * valueSize, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + + void + SetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr propValue, + XMP_OptionBits options ); + + void + SetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_StringPtr itemValue, + XMP_OptionBits options ); + + void + AppendArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + XMP_StringPtr itemValue, + XMP_OptionBits options ); + + void + SetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + XMP_OptionBits options ); + + void + SetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_StringPtr qualValue, + XMP_OptionBits options ); + + // --------------------------------------------------------------------------------------------- + + void + DeleteProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName ); + + void + DeleteArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex ); + + void + DeleteStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName ); + + void + DeleteQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName ); + + // --------------------------------------------------------------------------------------------- + + bool + DoesPropertyExist ( XMP_StringPtr schemaNS, + XMP_StringPtr propName ) const; + + bool + DoesArrayItemExist ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex ) const; + + bool + DoesStructFieldExist ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName ) const; + + bool + DoesQualifierExist ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName ) const; + + // --------------------------------------------------------------------------------------------- + + bool + GetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + XMP_StringPtr * actualLang, + XMP_StringLen * langSize, + XMP_StringPtr * itemValue, + XMP_StringLen * valueSize, + XMP_OptionBits * options ) const; + + void + SetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + XMP_StringPtr itemValue, + XMP_OptionBits options ); + + void + DeleteLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang); + + // --------------------------------------------------------------------------------------------- + + bool + GetProperty_Bool ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool * propValue, + XMP_OptionBits * options ) const; + + bool + GetProperty_Int ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 * propValue, + XMP_OptionBits * options ) const; + + bool + GetProperty_Int64 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 * propValue, + XMP_OptionBits * options ) const; + + bool + GetProperty_Float ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double * propValue, + XMP_OptionBits * options ) const; + + bool + GetProperty_Date ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_DateTime * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + + void + SetProperty_Bool ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool propValue, + XMP_OptionBits options ); + + void + SetProperty_Int ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 propValue, + XMP_OptionBits options ); + + void + SetProperty_Int64 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 propValue, + XMP_OptionBits options ); + + void + SetProperty_Float ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double propValue, + XMP_OptionBits options ); + + void + SetProperty_Date ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const XMP_DateTime & propValue, + XMP_OptionBits options ); + + // --------------------------------------------------------------------------------------------- + + void + GetObjectName ( XMP_StringPtr * namePtr, + XMP_StringLen * nameLen ) const; + + void + SetObjectName ( XMP_StringPtr name ); + + XMP_OptionBits + GetObjectOptions() const; + + void + SetObjectOptions ( XMP_OptionBits options ); + + void + Sort(); + + void + Erase(); + + void + Clone ( XMPMeta * clone, XMP_OptionBits options ) const; + + XMP_Index + CountArrayItems ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName ) const; + + void + DumpObject ( XMP_TextOutputProc outProc, + void * refCon ) const; + + // --------------------------------------------------------------------------------------------- + + void + ParseFromBuffer ( XMP_StringPtr buffer, + XMP_StringLen bufferSize, + XMP_OptionBits options ); + + void + SerializeToBuffer ( XMP_VarString * rdfString, + XMP_OptionBits options, + XMP_StringLen padding, + XMP_StringPtr newline, + XMP_StringPtr indent, + XMP_Index baseIndent ) const; + + // --------------------------------------------------------------------------------------------- + + static void + SetDefaultErrorCallback ( XMPMeta_ErrorCallbackWrapper wrapperProc, + XMPMeta_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit ); + + void + SetErrorCallback ( XMPMeta_ErrorCallbackWrapper wrapperProc, + XMPMeta_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit ); + + void + ResetErrorCallbackLimit ( XMP_Uns32 limit ); + + class ErrorCallbackInfo : public GenericErrorCallback { + public: + + XMPMeta_ErrorCallbackWrapper wrapperProc; + XMPMeta_ErrorCallbackProc clientProc; + void * context; + + ErrorCallbackInfo() : wrapperProc(0), clientProc(0), context(0) {}; + + void Clear() { this->wrapperProc = 0; this->clientProc = 0; this->context = 0; + GenericErrorCallback::Clear(); }; + + bool CanNotify() const; + bool ClientCallbackWrapper ( XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr messsage ) const; + }; + + // ============================================================================================= + + // --------------------------------------------------------------------------------------------- + // - Everything is built out of standard nodes. Each node has a name, value, option flags, a + // vector of child nodes, and a vector of qualifier nodes. + // + // - The option flags are those passed to SetProperty and returned from GetProperty. They tell + // if the node is simple, a struct or an array; whether it has qualifiers, etc. + // + // - The name of the node is an XML qualified name, of the form "prefix:simple-name". Since we + // force all namespaces to be known and to have unique prefixes, this is semantically equivalent + // to using a URI and simple name pair. + // + // - Although the value part is only for leaf properties and the children part is only for + // structs and arrays, it is easier to simply have them in every node. This keeps things visible + // so that debugging is easier + // + // - The top level node children are the namespaces that contain properties, the next level are + // the top level properties, lower levels are the fields of structs or items of arrays. The name + // of the top level nodes is just the namespace prefix, with the colon terminator. The name of + // top level properties includes the namespace prefix. + // + // - Any property node, at any level, can have qualifiers. These are themselves general property + // nodes. And could in fact themselves have qualifiers! + + // ! Expose the implementation so that file static functions can see the data. + + XMP_Int32 clientRefs; // ! Must be signed to allow decrement from 0. + XMP_ReadWriteLock lock; + + // ! Any data member changes must be propagted to the Clone function! + + XMP_Node tree; + XMLParserAdapter * xmlParser; + ErrorCallbackInfo errorCallback; + + friend class XMPIterator; + friend class XMPUtils; + +private: + + // ! These are hidden on purpose: + XMPMeta ( const XMPMeta & /* original */ ) : tree(XMP_Node(0,"",0)), clientRefs(0), xmlParser(0) + { XMP_Throw ( "Call to hidden constructor", kXMPErr_InternalFailure ); }; + void operator= ( const XMPMeta & /* rhs */ ) + { XMP_Throw ( "Call to hidden operator=", kXMPErr_InternalFailure ); }; + + // Special support routines for parsing, here to be able to access the errorCallback. + void ProcessXMLTree ( XMP_OptionBits options ); + bool ProcessXMLBuffer ( XMP_StringPtr buffer, XMP_StringLen xmpSize, bool lastClientCall ); + void ProcessRDF ( const XML_Node & xmlTree, XMP_OptionBits options ); + +}; // class XMPMeta + +// ================================================================================================= + +#endif // __XMPMeta_hpp__ diff --git a/source/lib/xmp_core/XMPUtils-FileInfo.cpp b/source/lib/xmp_core/XMPUtils-FileInfo.cpp new file mode 100644 index 0000000..19096dc --- /dev/null +++ b/source/lib/xmp_core/XMPUtils-FileInfo.cpp @@ -0,0 +1,1493 @@ +// ================================================================================================= +// Copyright 2003 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include // For binary_search. + +#include "public/include/XMP_Environment.h" // ! This must be the first include! +#include "XMPCore_Impl.hpp" + +#include "XMPUtils.hpp" + +#include +#include +#include +#include +#include + +#include // For snprintf. + +#if XMP_WinBuild + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) +#endif + +// ================================================================================================= +// Local Types and Constants +// ========================= + +typedef unsigned long UniCodePoint; + +enum UniCharKind { + UCK_normal, + UCK_space, + UCK_comma, + UCK_semicolon, + UCK_quote, + UCK_control +}; +typedef enum UniCharKind UniCharKind; + +#define UnsByte(c) ((unsigned char)(c)) +#define UCP(u) ((UniCodePoint)(u)) + // ! Needed on Windows (& PC Linux?) for inequalities with literals ito avoid sign extension. + +#ifndef TraceMultiFile + #define TraceMultiFile 0 +#endif + +// ================================================================================================= +// Static Variables +// ================ + +// ================================================================================================= +// Local Utilities +// =============== + +// ------------------------------------------------------------------------------------------------- +// ClassifyCharacter +// ----------------- + +static void +ClassifyCharacter ( XMP_StringPtr fullString, size_t offset, + UniCharKind * charKind, size_t * charSize, UniCodePoint * uniChar ) +{ + *charKind = UCK_normal; // Assume typical case. + + unsigned char currByte = UnsByte ( fullString[offset] ); + + if ( currByte < UnsByte(0x80) ) { + + // ---------------------------------------- + // We've got a single byte ASCII character. + + *charSize = 1; + *uniChar = currByte; + + if ( currByte > UnsByte(0x22) ) { + + if ( currByte == UnsByte(0x2C) ) { + *charKind = UCK_comma; + } else if ( currByte == UnsByte(0x3B) ) { + *charKind = UCK_semicolon; + } + // [2674672] Discontinue to interpret square brackets + // as Asian quotes in XMPUtils::SeparateArrayItems(..)) + // *** else if ( (currByte == UnsByte(0x5B)) || (currByte == UnsByte(0x5D)) ) { + // *** *charKind = UCK_quote; // ! ASCII '[' and ']' are used as quotes in Chinese and Korean. + // *** } + + } else { // currByte <= 0x22 + + if ( currByte == UnsByte(0x22) ) { + *charKind = UCK_quote; + } else if ( currByte == UnsByte(0x21) ) { + *charKind = UCK_normal; + } else if ( currByte == UnsByte(0x20) ) { + *charKind = UCK_space; + } else { + *charKind = UCK_control; + } + + } + + } else { // currByte >= 0x80 + + // --------------------------------------------------------------------------------------- + // We've got a multibyte Unicode character. The first byte has the number of bytes and the + // highest order bits. The other bytes each add 6 more bits. Compose the UTF-32 form so we + // can classify directly with the Unicode code points. Order the upperBits tests to be + // fastest for Japan, probably the most common non-ASCII usage. + + *charSize = 0; + *uniChar = currByte; + while ( (*uniChar & 0x80) != 0 ) { // Count the leading 1 bits in the byte. + ++(*charSize); + *uniChar = *uniChar << 1; + } + XMP_Assert ( (offset + *charSize) <= strlen(fullString) ); + + *uniChar = *uniChar & 0x7F; // Put the character bits in the bottom of uniChar. + *uniChar = *uniChar >> *charSize; + + for ( size_t i = (offset + 1); i < (offset + *charSize); ++i ) { + *uniChar = (*uniChar << 6) | (UnsByte(fullString[i]) & 0x3F); + } + + XMP_Uns32 upperBits = *uniChar >> 8; // First filter on just the high order 24 bits. + + if ( upperBits == 0xFF ) { // U+FFxx + + if ( *uniChar == UCP(0xFF0C) ) { + *charKind = UCK_comma; // U+FF0C, full width comma. + } else if ( *uniChar == UCP(0xFF1B) ) { + *charKind = UCK_semicolon; // U+FF1B, full width semicolon. + } else if ( *uniChar == UCP(0xFF64) ) { + *charKind = UCK_comma; // U+FF64, half width ideographic comma. + } + + } else if ( upperBits == 0xFE ) { // U+FE-- + + if ( *uniChar == UCP(0xFE50) ) { + *charKind = UCK_comma; // U+FE50, small comma. + } else if ( *uniChar == UCP(0xFE51) ) { + *charKind = UCK_comma; // U+FE51, small ideographic comma. + } else if ( *uniChar == UCP(0xFE54) ) { + *charKind = UCK_semicolon; // U+FE54, small semicolon. + } + + } else if ( upperBits == 0x30 ) { // U+30-- + + if ( *uniChar == UCP(0x3000) ) { + *charKind = UCK_space; // U+3000, ideographic space. + } else if ( *uniChar == UCP(0x3001) ) { + *charKind = UCK_comma; // U+3001, ideographic comma. + } else if ( (UCP(0x3008) <= *uniChar) && (*uniChar <= UCP(0x300F)) ) { + *charKind = UCK_quote; // U+3008..U+300F, various quotes. + } else if ( *uniChar == UCP(0x303F) ) { + *charKind = UCK_space; // U+303F, ideographic half fill space. + } else if ( (UCP(0x301D) <= *uniChar) && (*uniChar <= UCP(0x301F)) ) { + *charKind = UCK_quote; // U+301D..U+301F, double prime quotes. + } + + } else if ( upperBits == 0x20 ) { // U+20-- + + if ( (UCP(0x2000) <= *uniChar) && (*uniChar <= UCP(0x200B)) ) { + *charKind = UCK_space; // U+2000..U+200B, en quad through zero width space. + } else if ( *uniChar == UCP(0x2015) ) { + *charKind = UCK_quote; // U+2015, dash quote. + } else if ( (UCP(0x2018) <= *uniChar) && (*uniChar <= UCP(0x201F)) ) { + *charKind = UCK_quote; // U+2018..U+201F, various quotes. + } else if ( *uniChar == UCP(0x2028) ) { + *charKind = UCK_control; // U+2028, line separator. + } else if ( *uniChar == UCP(0x2029) ) { + *charKind = UCK_control; // U+2029, paragraph separator. + } else if ( (*uniChar == UCP(0x2039)) || (*uniChar == UCP(0x203A)) ) { + *charKind = UCK_quote; // U+2039 and U+203A, guillemet quotes. + } + + } else if ( upperBits == 0x06 ) { // U+06-- + + if ( *uniChar == UCP(0x060C) ) { + *charKind = UCK_comma; // U+060C, Arabic comma. + } else if ( *uniChar == UCP(0x061B) ) { + *charKind = UCK_semicolon; // U+061B, Arabic semicolon. + } + + } else if ( upperBits == 0x05 ) { // U+05-- + + if ( *uniChar == UCP(0x055D) ) { + *charKind = UCK_comma; // U+055D, Armenian comma. + } + + } else if ( upperBits == 0x03 ) { // U+03-- + + if ( *uniChar == UCP(0x037E) ) { + *charKind = UCK_semicolon; // U+037E, Greek "semicolon" (really a question mark). + } + + } else if ( upperBits == 0x00 ) { // U+00-- + + if ( (*uniChar == UCP(0x00AB)) || (*uniChar == UCP(0x00BB)) ) { + *charKind = UCK_quote; // U+00AB and U+00BB, guillemet quotes. + } + + } + + } + +} // ClassifyCharacter + + +// ------------------------------------------------------------------------------------------------- +// IsClosingingQuote +// ----------------- + +static inline bool +IsClosingingQuote ( UniCodePoint uniChar, UniCodePoint openQuote, UniCodePoint closeQuote ) +{ + + if ( (uniChar == closeQuote) || + ( (openQuote == UCP(0x301D)) && ((uniChar == UCP(0x301E)) || (uniChar == UCP(0x301F))) ) ) { + return true; + } else { + return false; + } + +} // IsClosingingQuote + + +// ------------------------------------------------------------------------------------------------- +// IsSurroundingQuote +// ------------------ + +static inline bool +IsSurroundingQuote ( UniCodePoint uniChar, UniCodePoint openQuote, UniCodePoint closeQuote ) +{ + + if ( (uniChar == openQuote) || IsClosingingQuote ( uniChar, openQuote, closeQuote ) ) { + return true; + } else { + return false; + } + +} // IsSurroundingQuote + + +// ------------------------------------------------------------------------------------------------- +// GetClosingQuote +// --------------- + +static UniCodePoint +GetClosingQuote ( UniCodePoint openQuote ) +{ + UniCodePoint closeQuote; + + switch ( openQuote ) { + + case UCP(0x0022) : closeQuote = UCP(0x0022); // ! U+0022 is both opening and closing. + break; + // *** [2674672] Discontinue to interpret square brackets + // *** as Asian quotes in XMPUtils::SeparateArrayItems(..)) + // *** case UCP(0x005B) : closeQuote = UCP(0x005D); + // *** break; + case UCP(0x00AB) : closeQuote = UCP(0x00BB); // ! U+00AB and U+00BB are reversible. + break; + case UCP(0x00BB) : closeQuote = UCP(0x00AB); + break; + case UCP(0x2015) : closeQuote = UCP(0x2015); // ! U+2015 is both opening and closing. + break; + case UCP(0x2018) : closeQuote = UCP(0x2019); + break; + case UCP(0x201A) : closeQuote = UCP(0x201B); + break; + case UCP(0x201C) : closeQuote = UCP(0x201D); + break; + case UCP(0x201E) : closeQuote = UCP(0x201F); + break; + case UCP(0x2039) : closeQuote = UCP(0x203A); // ! U+2039 and U+203A are reversible. + break; + case UCP(0x203A) : closeQuote = UCP(0x2039); + break; + case UCP(0x3008) : closeQuote = UCP(0x3009); + break; + case UCP(0x300A) : closeQuote = UCP(0x300B); + break; + case UCP(0x300C) : closeQuote = UCP(0x300D); + break; + case UCP(0x300E) : closeQuote = UCP(0x300F); + break; + case UCP(0x301D) : closeQuote = UCP(0x301F); // ! U+301E also closes U+301D. + break; + default : closeQuote = 0; + break; + + } + + return closeQuote; + +} // GetClosingQuote + + +// ------------------------------------------------------------------------------------------------- +// CodePointToUTF8 +// --------------- + +static void +CodePointToUTF8 ( UniCodePoint uniChar, XMP_VarString & utf8Str ) +{ + size_t i, byteCount; + XMP_Uns8 buffer [8]; + UniCodePoint cpTemp; + + if ( uniChar <= 0x7F ) { + + i = 7; + byteCount = 1; + buffer[7] = char(uniChar); + + } else { + + // --------------------------------------------------------------------------------------- + // Copy the data bits from the low order end to the high order end, include the 0x80 mask. + + i = 8; + cpTemp = uniChar; + while ( cpTemp != 0 ) { + -- i; // Exit with i pointing to the last byte stored. + buffer[i] = UnsByte(0x80) | (UnsByte(cpTemp) & 0x3F); + cpTemp = cpTemp >> 6; + } + byteCount = 8 - i; // The total number of bytes needed. + XMP_Assert ( (2 <= byteCount) && (byteCount <= 6) ); + + // ------------------------------------------------------------------------------------- + // Make sure the high order byte can hold the byte count mask, compute and set the mask. + + size_t bitCount = 0; // The number of data bits in the first byte. + for ( cpTemp = (buffer[i] & UnsByte(0x3F)); cpTemp != 0; cpTemp = cpTemp >> 1 ) bitCount += 1; + if ( bitCount > (8 - (byteCount + 1)) ) byteCount += 1; + + i = 8 - byteCount; // First byte index and mask shift count. + XMP_Assert ( (0 <= i) && (i <= 6) ); + buffer[i] |= (UnsByte(0xFF) << i) & UnsByte(0xFF); // AUDIT: Safe, i is between 0 and 6. + + } + + utf8Str.assign ( (char*)(&buffer[i]), byteCount ); + +} // CodePointToUTF8 + + +// ------------------------------------------------------------------------------------------------- +// ApplyQuotes +// ----------- + +static void +ApplyQuotes ( XMP_VarString * item, UniCodePoint openQuote, UniCodePoint closeQuote, bool allowCommas ) +{ + bool prevSpace = false; + size_t charOffset, charLen; + UniCharKind charKind; + UniCodePoint uniChar; + + // ----------------------------------------------------------------------------------------- + // See if there are any separators in the value. Stop at the first occurrance. This is a bit + // tricky in order to make typical typing work conveniently. The purpose of applying quotes + // is to preserve the values when splitting them back apart. That is CatenateContainerItems + // and SeparateContainerItems must round trip properly. For the most part we only look for + // separators here. Internal quotes, as in -- Irving "Bud" Jones -- won't cause problems in + // the separation. An initial quote will though, it will make the value look quoted. + + charOffset = 0; + ClassifyCharacter ( item->c_str(), charOffset, &charKind, &charLen, &uniChar ); + + if ( charKind != UCK_quote ) { + + for ( charOffset = 0; size_t(charOffset) < item->size(); charOffset += charLen ) { + + ClassifyCharacter ( item->c_str(), charOffset, &charKind, &charLen, &uniChar ); + + if ( charKind == UCK_space ) { + if ( prevSpace ) break; // Multiple spaces are a separator. + prevSpace = true; + } else { + prevSpace = false; + if ( (charKind == UCK_semicolon) || (charKind == UCK_control) ) break; + if ( (charKind == UCK_comma) && (! allowCommas) ) break; + } + + } + + } + + if ( size_t(charOffset) < item->size() ) { + + // -------------------------------------------------------------------------------------- + // Create a quoted copy, doubling any internal quotes that match the outer ones. Internal + // quotes did not stop the "needs quoting" search, but they do need doubling. So we have + // to rescan the front of the string for quotes. Handle the special case of U+301D being + // closed by either U+301E or U+301F. + + XMP_VarString newItem; + size_t splitPoint; + + for ( splitPoint = 0; splitPoint <= charOffset; ++splitPoint ) { + ClassifyCharacter ( item->c_str(), splitPoint, &charKind, &charLen, &uniChar ); + if ( charKind == UCK_quote ) break; + } + + CodePointToUTF8 ( openQuote, newItem ); + newItem.append ( *item, 0, splitPoint ); // Copy the leading "normal" portion. + + for ( charOffset = splitPoint; size_t(charOffset) < item->size(); charOffset += charLen ) { + ClassifyCharacter ( item->c_str(), charOffset, &charKind, &charLen, &uniChar ); + newItem.append ( *item, charOffset, charLen ); + if ( (charKind == UCK_quote) && IsSurroundingQuote ( uniChar, openQuote, closeQuote ) ) { + newItem.append ( *item, charOffset, charLen ); + } + } + + XMP_VarString closeStr; + CodePointToUTF8 ( closeQuote, closeStr ); + newItem.append ( closeStr ); + + *item = newItem; + + } + +} // ApplyQuotes + + +// ------------------------------------------------------------------------------------------------- +// IsInternalProperty +// ------------------ + +// *** Need static checks of the schema prefixes! + +static const char * kExternalxmpDM[] = + { "xmpDM:album", + "xmpDM:altTapeName", + "xmpDM:altTimecode", + "xmpDM:artist", + "xmpDM:cameraAngle", + "xmpDM:cameraLabel", + "xmpDM:cameraModel", + "xmpDM:cameraMove", + "xmpDM:client", + "xmpDM:comment", + "xmpDM:composer", + "xmpDM:director", + "xmpDM:directorPhotography", + "xmpDM:engineer", + "xmpDM:genre", + "xmpDM:good", + "xmpDM:instrument", + "xmpDM:logComment", + "xmpDM:projectName", + "xmpDM:releaseDate", + "xmpDM:scene", + "xmpDM:shotDate", + "xmpDM:shotDay", + "xmpDM:shotLocation", + "xmpDM:shotName", + "xmpDM:shotNumber", + "xmpDM:shotSize", + "xmpDM:speakerPlacement", + "xmpDM:takeNumber", + "xmpDM:tapeName", + "xmpDM:trackNumber", + "xmpDM:videoAlphaMode", + "xmpDM:videoAlphaPremultipleColor", + 0 }; // ! Must have zero sentinel! + +typedef const char ** CharStarIterator; // Used for binary search of kExternalxmpDM; +static const char ** kLastExternalxmpDM = 0; // Set on first use. +static int CharStarLess (const char * left, const char * right ) + { return (strcmp ( left, right ) < 0); } + +#define IsExternalProperty(s,p) (! IsInternalProperty ( s, p )) + +static bool +IsInternalProperty ( const XMP_VarString & schema, const XMP_VarString & prop ) +{ + bool isInternal = false; + + if ( schema == kXMP_NS_DC ) { + + if ( (prop == "dc:format") || + (prop == "dc:language") ) { + isInternal = true; + } + + } else if ( schema == kXMP_NS_XMP ) { + + if ( (prop == "xmp:BaseURL") || + (prop == "xmp:CreatorTool") || + (prop == "xmp:Format") || + (prop == "xmp:Locale") || + (prop == "xmp:MetadataDate") || + (prop == "xmp:ModifyDate") ) { + isInternal = true; + } + + } else if ( schema == kXMP_NS_PDF ) { + + if ( (prop == "pdf:BaseURL") || + (prop == "pdf:Creator") || + (prop == "pdf:ModDate") || + (prop == "pdf:PDFVersion") || + (prop == "pdf:Producer") ) { + isInternal = true; + } + + } else if ( schema == kXMP_NS_TIFF ) { + + isInternal = true; // ! The TIFF properties are internal by default. + if ( (prop == "tiff:ImageDescription") || // ! ImageDescription, Artist, and Copyright are aliased. + (prop == "tiff:Artist") || + (prop == "tiff:Copyright") ) { + isInternal = false; + } + + } else if ( schema == kXMP_NS_EXIF ) { + + isInternal = true; // ! The EXIF properties are internal by default. + if ( prop == "exif:UserComment" ) isInternal = false; + + } else if ( schema == kXMP_NS_EXIF_Aux ) { + + isInternal = true; // ! The EXIF Aux properties are internal by default. + + } else if ( schema == kXMP_NS_Photoshop ) { + + if ( (prop == "photoshop:ICCProfile") || + (prop == "photoshop:TextLayers") ) isInternal = true; + + } else if ( schema == kXMP_NS_CameraRaw ) { + + isInternal = true; // All of crs: is internal, they are processing settings. + + } else if ( schema == kXMP_NS_DM ) { + + // ! Most of the xmpDM schema is internal, and unknown properties default to internal. + if ( kLastExternalxmpDM == 0 ) { + for ( kLastExternalxmpDM = &kExternalxmpDM[0]; *kLastExternalxmpDM != 0; ++kLastExternalxmpDM ) {} + } + isInternal = (! std::binary_search ( &kExternalxmpDM[0], kLastExternalxmpDM, prop.c_str(), CharStarLess )); + + } else if ( schema == kXMP_NS_Script ) { + + isInternal = true; // ! Most of the xmpScript schema is internal, and unknown properties default to internal. + if ( (prop == "xmpScript:action") || (prop == "xmpScript:character") || (prop == "xmpScript:dialog") || + (prop == "xmpScript:sceneSetting") || (prop == "xmpScript:sceneTimeOfDay") ) { + isInternal = false; + } + + } else if ( schema == kXMP_NS_BWF ) { + + if ( prop == "bext:version" ) isInternal = true; + + } else if ( schema == kXMP_NS_AdobeStockPhoto ) { + + isInternal = true; // ! The bmsp schema has only internal properties. + + } else if ( schema == kXMP_NS_XMP_MM ) { + + isInternal = true; // ! The xmpMM schema has only internal properties. + + } else if ( schema == kXMP_NS_XMP_Text ) { + + isInternal = true; // ! The xmpT schema has only internal properties. + + } else if ( schema == kXMP_NS_XMP_PagedFile ) { + + isInternal = true; // ! The xmpTPg schema has only internal properties. + + } else if ( schema == kXMP_NS_XMP_Graphics ) { + + isInternal = true; // ! The xmpG schema has only internal properties. + + } else if ( schema == kXMP_NS_XMP_Image ) { + + isInternal = true; // ! The xmpGImg schema has only internal properties. + + } else if ( schema == kXMP_NS_XMP_Font ) { + + isInternal = true; // ! The stFNT schema has only internal properties. + + } + + return isInternal; + +} // IsInternalProperty + + +// ------------------------------------------------------------------------------------------------- +// RemoveSchemaChildren +// -------------------- + +static void +RemoveSchemaChildren ( XMP_NodePtrPos schemaPos, bool doAll ) +{ + XMP_Node * schemaNode = *schemaPos; + XMP_Assert ( XMP_NodeIsSchema ( schemaNode->options ) ); + + // ! Iterate backwards to reduce shuffling as children are erased and to simplify the logic for + // ! denoting the current child. (Erasing child n makes the old n+1 now be n.) + + size_t propCount = schemaNode->children.size(); + XMP_NodePtrPos beginPos = schemaNode->children.begin(); + + for ( size_t propNum = propCount-1, propLim = (size_t)(-1); propNum != propLim; --propNum ) { + XMP_NodePtrPos currProp = beginPos + propNum; + if ( doAll || IsExternalProperty ( schemaNode->name, (*currProp)->name ) ) { + delete *currProp; // ! Both delete the node and erase the pointer from the parent. + schemaNode->children.erase ( currProp ); + } + } + + if ( schemaNode->children.empty() ) { + XMP_Node * tree = schemaNode->parent; + tree->children.erase ( schemaPos ); + delete schemaNode; + } + +} // RemoveSchemaChildren + + +// ------------------------------------------------------------------------------------------------- +// ItemValuesMatch +// --------------- +// +// Does the value comparisons for array merging as part of XMPUtils::AppendProperties. + +static bool +ItemValuesMatch ( const XMP_Node * leftNode, const XMP_Node * rightNode ) +{ + const XMP_OptionBits leftForm = leftNode->options & kXMP_PropCompositeMask; + const XMP_OptionBits rightForm = leftNode->options & kXMP_PropCompositeMask; + + if ( leftForm != rightForm ) return false; + + if ( leftForm == 0 ) { + + // Simple nodes, check the values and xml:lang qualifiers. + + if ( leftNode->value != rightNode->value ) return false; + if ( (leftNode->options & kXMP_PropHasLang) != (rightNode->options & kXMP_PropHasLang) ) return false; + if ( leftNode->options & kXMP_PropHasLang ) { + if ( leftNode->qualifiers[0]->value != rightNode->qualifiers[0]->value ) return false; + } + + } else if ( leftForm == kXMP_PropValueIsStruct ) { + + // Struct nodes, see if all fields match, ignoring order. + + if ( leftNode->children.size() != rightNode->children.size() ) return false; + + for ( size_t leftNum = 0, leftLim = leftNode->children.size(); leftNum != leftLim; ++leftNum ) { + const XMP_Node * leftField = leftNode->children[leftNum]; + const XMP_Node * rightField = FindConstChild ( rightNode, leftField->name.c_str() ); + if ( (rightField == 0) || (! ItemValuesMatch ( leftField, rightField )) ) return false; + } + + } else { + + // Array nodes, see if the "leftNode" values are present in the "rightNode", ignoring order, duplicates, + // and extra values in the rightNode-> The rightNode is the destination for AppendProperties. + + XMP_Assert ( leftForm & kXMP_PropValueIsArray ); + + for ( size_t leftNum = 0, leftLim = leftNode->children.size(); leftNum != leftLim; ++leftNum ) { + + const XMP_Node * leftItem = leftNode->children[leftNum]; + + size_t rightNum, rightLim; + for ( rightNum = 0, rightLim = rightNode->children.size(); rightNum != rightLim; ++rightNum ) { + const XMP_Node * rightItem = rightNode->children[rightNum]; + if ( ItemValuesMatch ( leftItem, rightItem ) ) break; + } + if ( rightNum == rightLim ) return false; + + } + + } + + return true; // All of the checks passed. + +} // ItemValuesMatch + + +// ------------------------------------------------------------------------------------------------- +// AppendSubtree +// ------------- +// +// The main implementation of XMPUtils::AppendProperties. See the description in TXMPMeta.hpp. + +static void +AppendSubtree ( const XMP_Node * sourceNode, XMP_Node * destParent, + const bool mergeCompound, const bool replaceOld, const bool deleteEmpty ) +{ + XMP_NodePtrPos destPos; + XMP_Node * destNode = FindChildNode ( destParent, sourceNode->name.c_str(), kXMP_ExistingOnly, &destPos ); + + bool valueIsEmpty = false; + if ( XMP_PropIsSimple ( sourceNode->options ) ) { + valueIsEmpty = sourceNode->value.empty(); + } else { + valueIsEmpty = sourceNode->children.empty(); + } + + if ( valueIsEmpty ) { + if ( deleteEmpty && (destNode != 0) ) { + delete ( destNode ); + destParent->children.erase ( destPos ); + } + return; // ! Done, empty values are either ignored or cause deletions. + } + + if ( destNode == 0 ) { + // The one easy case, the destination does not exist. + destNode = CloneSubtree ( sourceNode, destParent, true /* skipEmpty */ ); + XMP_Assert ( (destNode == 0) || (! destNode->value.empty()) || (! destNode->children.empty()) ); + return; + } + + // If we get here we're going to modify an existing property, either replacing or merging. + + XMP_Assert ( (! valueIsEmpty) && (destNode != 0) ); + + XMP_OptionBits sourceForm = sourceNode->options & kXMP_PropCompositeMask; + XMP_OptionBits destForm = destNode->options & kXMP_PropCompositeMask; + + bool replaceThis = replaceOld; // ! Don't modify replaceOld, it gets passed to inner calls. + if ( mergeCompound && (! XMP_PropIsSimple ( sourceForm )) ) replaceThis = false; + + if ( replaceThis ) { + + destNode->value = sourceNode->value; // *** Should use SetNode. + destNode->options = sourceNode->options; + destNode->RemoveChildren(); + destNode->RemoveQualifiers(); + CloneOffspring ( sourceNode, destNode, true /* skipEmpty */ ); + + if ( (! XMP_PropIsSimple ( destNode->options )) && destNode->children.empty() ) { + // Don't keep an empty array or struct. The source might be implicitly empty due to + // all children being empty. In this case CloneOffspring should skip them. + DeleteSubtree ( destPos ); + } + + return; + + } + + // From here on are cases for merging arrays or structs. + + if ( XMP_PropIsSimple ( sourceForm ) || (sourceForm != destForm) ) return; + + if ( sourceForm == kXMP_PropValueIsStruct ) { + + // To merge a struct process the fields recursively. E.g. add simple missing fields. The + // recursive call to AppendSubtree will handle deletion for fields with empty values. + + for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim && destNode!= NULL; ++sourceNum ) { + const XMP_Node * sourceField = sourceNode->children[sourceNum]; + AppendSubtree ( sourceField, destNode, mergeCompound, replaceOld, deleteEmpty ); + if ( deleteEmpty && destNode->children.empty() ) { + delete ( destNode ); + destParent->children.erase ( destPos ); + } + } + + } else if ( sourceForm & kXMP_PropArrayIsAltText ) { + + // Merge AltText arrays by the xml:lang qualifiers. Make sure x-default is first. Make a + // special check for deletion of empty values. Meaningful in AltText arrays because the + // xml:lang qualifier provides unambiguous source/dest correspondence. + + XMP_Assert ( mergeCompound ); + + for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim && destNode!= NULL; ++sourceNum ) { + + const XMP_Node * sourceItem = sourceNode->children[sourceNum]; + if ( sourceItem->qualifiers.empty() || (sourceItem->qualifiers[0]->name != "xml:lang") ) continue; + + XMP_Index destIndex = LookupLangItem ( destNode, sourceItem->qualifiers[0]->value ); + + if ( sourceItem->value.empty() ) { + + if ( deleteEmpty && (destIndex != -1) ) { + delete ( destNode->children[destIndex] ); + destNode->children.erase ( destNode->children.begin() + destIndex ); + if ( destNode->children.empty() ) { + delete ( destNode ); + destParent->children.erase ( destPos ); + } + } + + } else { + + if ( destIndex != -1 ) { + + // The source and dest arrays both have this language item. + + if ( replaceOld ) { // ! Yes, check replaceOld not replaceThis! + destNode->children[destIndex]->value = sourceItem->value; + } + + } else { + + // The dest array does not have this language item, add it. + + if ( (sourceItem->qualifiers[0]->value != "x-default") || destNode->children.empty() ) { + // Typical case, empty dest array or not "x-default". Non-empty should always have "x-default". + CloneSubtree ( sourceItem, destNode, true /* skipEmpty */ ); + } else { + // Edge case, non-empty dest array had no "x-default", insert that at the beginning. + XMP_Node * destItem = new XMP_Node ( destNode, sourceItem->name, sourceItem->value, sourceItem->options ); + CloneOffspring ( sourceItem, destItem, true /* skipEmpty */ ); + destNode->children.insert ( destNode->children.begin(), destItem ); + } + + } + + } + + } + + } else if ( sourceForm & kXMP_PropValueIsArray ) { + + // Merge other arrays by item values. Don't worry about order or duplicates. Source + // items with empty values do not cause deletion, that conflicts horribly with merging. + + for ( size_t sourceNum = 0, sourceLim = sourceNode->children.size(); sourceNum != sourceLim; ++sourceNum ) { + const XMP_Node * sourceItem = sourceNode->children[sourceNum]; + + size_t destNum, destLim; + for ( destNum = 0, destLim = destNode->children.size(); destNum != destLim; ++destNum ) { + const XMP_Node * destItem = destNode->children[destNum]; + if ( ItemValuesMatch ( sourceItem, destItem ) ) break; + } + if ( destNum == destLim ) CloneSubtree ( sourceItem, destNode, true /* skipEmpty */ ); + + } + + } + +} // AppendSubtree + + +// ================================================================================================= +// Class Static Functions +// ====================== + +// ------------------------------------------------------------------------------------------------- +// CatenateArrayItems +// ------------------ + +/* class static */ void +XMPUtils::CatenateArrayItems ( const XMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr separator, + XMP_StringPtr quotes, + XMP_OptionBits options, + XMP_VarString * catedStr ) +{ + XMP_Assert ( (schemaNS != 0) && (arrayName != 0) ); // ! Enforced by wrapper. + XMP_Assert ( (separator != 0) && (quotes != 0) && (catedStr != 0) ); // ! Enforced by wrapper. + + size_t strLen, strPos, charLen; + UniCharKind charKind; + UniCodePoint currUCP, openQuote, closeQuote; + + const bool allowCommas = ((options & kXMPUtil_AllowCommas) != 0); + + const XMP_Node * arrayNode = 0; // ! Move up to avoid gcc complaints. + XMP_OptionBits arrayForm = 0; + const XMP_Node * currItem = 0; + + // Make sure the separator is OK. It must be one semicolon surrounded by zero or more spaces. + // Any of the recognized semicolons or spaces are allowed. + + strPos = 0; + strLen = strlen ( separator ); + bool haveSemicolon = false; + + while ( strPos < strLen ) { + ClassifyCharacter ( separator, strPos, &charKind, &charLen, &currUCP ); + strPos += charLen; + if ( charKind == UCK_semicolon ) { + if ( haveSemicolon ) XMP_Throw ( "Separator can have only one semicolon", kXMPErr_BadParam ); + haveSemicolon = true; + } else if ( charKind != UCK_space ) { + XMP_Throw ( "Separator can have only spaces and one semicolon", kXMPErr_BadParam ); + } + }; + if ( ! haveSemicolon ) XMP_Throw ( "Separator must have one semicolon", kXMPErr_BadParam ); + + // Make sure the open and close quotes are a legitimate pair. + + strLen = strlen ( quotes ); + ClassifyCharacter ( quotes, 0, &charKind, &charLen, &openQuote ); + if ( charKind != UCK_quote ) XMP_Throw ( "Invalid quoting character", kXMPErr_BadParam ); + + if ( charLen == strLen ) { + closeQuote = openQuote; + } else { + strPos = charLen; + ClassifyCharacter ( quotes, strPos, &charKind, &charLen, &closeQuote ); + if ( charKind != UCK_quote ) XMP_Throw ( "Invalid quoting character", kXMPErr_BadParam ); + if ( (strPos + charLen) != strLen ) XMP_Throw ( "Quoting string too long", kXMPErr_BadParam ); + } + if ( closeQuote != GetClosingQuote ( openQuote ) ) XMP_Throw ( "Mismatched quote pair", kXMPErr_BadParam ); + + // Return an empty result if the array does not exist, hurl if it isn't the right form. + + catedStr->erase(); + + XMP_ExpandedXPath arrayPath; + ExpandXPath ( schemaNS, arrayName, &arrayPath ); + + arrayNode = FindConstNode ( &xmpObj.tree, arrayPath ); + if ( arrayNode == 0 ) return; + + arrayForm = arrayNode->options & kXMP_PropCompositeMask; + if ( (! (arrayForm & kXMP_PropValueIsArray)) || (arrayForm & kXMP_PropArrayIsAlternate) ) { + XMP_Throw ( "Named property must be non-alternate array", kXMPErr_BadParam ); + } + if ( arrayNode->children.empty() ) return; + + // Build the result, quoting the array items, adding separators. Hurl if any item isn't simple. + // Start the result with the first value, then add the rest with a preceeding separator. + + currItem = arrayNode->children[0]; + + if ( (currItem->options & kXMP_PropCompositeMask) != 0 ) XMP_Throw ( "Array items must be simple", kXMPErr_BadParam ); + *catedStr = currItem->value; + ApplyQuotes ( catedStr, openQuote, closeQuote, allowCommas ); + + for ( size_t itemNum = 1, itemLim = arrayNode->children.size(); itemNum != itemLim; ++itemNum ) { + const XMP_Node * item = arrayNode->children[itemNum]; + if ( (item->options & kXMP_PropCompositeMask) != 0 ) XMP_Throw ( "Array items must be simple", kXMPErr_BadParam ); + XMP_VarString tempStr ( item->value ); + ApplyQuotes ( &tempStr, openQuote, closeQuote, allowCommas ); + *catedStr += separator; + *catedStr += tempStr; + } + +} // CatenateArrayItems + + +// ------------------------------------------------------------------------------------------------- +// SeparateArrayItems +// ------------------ + +/* class static */ void +XMPUtils::SeparateArrayItems ( XMPMeta * xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + XMP_StringPtr catedStr ) +{ + XMP_Assert ( (schemaNS != 0) && (arrayName != 0) && (catedStr != 0) ); // ! Enforced by wrapper. + + XMP_VarString itemValue; + size_t itemStart, itemEnd; + size_t nextSize, charSize = 0; // Avoid VS uninit var warnings. + UniCharKind nextKind, charKind = UCK_normal; + UniCodePoint nextChar, uniChar = 0; + + // Extract "special" option bits, verify and normalize the others. + + bool preserveCommas = false; + if ( options & kXMPUtil_AllowCommas ) { + preserveCommas = true; + options ^= kXMPUtil_AllowCommas; + } + + options = VerifySetOptions ( options, 0 ); // Keep a zero value, has special meaning below. + if ( options & ~kXMP_PropArrayFormMask ) XMP_Throw ( "Options can only provide array form", kXMPErr_BadOptions ); + + // Find the array node, make sure it is OK. Move the current children aside, to be readded later if kept. + + XMP_ExpandedXPath arrayPath; + ExpandXPath ( schemaNS, arrayName, &arrayPath ); + XMP_Node * arrayNode = FindNode ( &xmpObj->tree, arrayPath, kXMP_ExistingOnly ); + + if ( arrayNode != 0 ) { + // The array exists, make sure the form is compatible. Zero arrayForm means take what exists. + XMP_OptionBits arrayForm = arrayNode->options & kXMP_PropArrayFormMask; + if ( (arrayForm == 0) || (arrayForm & kXMP_PropArrayIsAlternate) ) { + XMP_Throw ( "Named property must be non-alternate array", kXMPErr_BadXPath ); + } + if ( (options != 0) && (options != arrayForm) ) XMP_Throw ( "Mismatch of specified and existing array form", kXMPErr_BadXPath ); // *** Right error? + } else { + // The array does not exist, try to create it. + arrayNode = FindNode ( &xmpObj->tree, arrayPath, kXMP_CreateNodes, (options | kXMP_PropValueIsArray) ); + if ( arrayNode == 0 ) XMP_Throw ( "Failed to create named array", kXMPErr_BadXPath ); + } + + XMP_NodeOffspring oldChildren ( arrayNode->children ); + size_t oldChildCount = oldChildren.size(); + arrayNode->children.clear(); + + // Extract the item values one at a time, until the whole input string is done. Be very careful + // in the extraction about the string positions. They are essentially byte pointers, while the + // contents are UTF-8. Adding or subtracting 1 does not necessarily move 1 Unicode character! + + size_t endPos = strlen ( catedStr ); + + itemEnd = 0; + while ( itemEnd < endPos ) { + + // Skip any leading spaces and separation characters. Always skip commas here. They can be + // kept when within a value, but not when alone between values. + + for ( itemStart = itemEnd; itemStart < endPos; itemStart += charSize ) { + ClassifyCharacter ( catedStr, itemStart, &charKind, &charSize, &uniChar ); + if ( (charKind == UCK_normal) || (charKind == UCK_quote) ) break; + } + if ( itemStart >= endPos ) break; + + if ( charKind != UCK_quote ) { + + // This is not a quoted value. Scan for the end, create an array item from the substring. + + for ( itemEnd = itemStart; itemEnd < endPos; itemEnd += charSize ) { + + ClassifyCharacter ( catedStr, itemEnd, &charKind, &charSize, &uniChar ); + + if ( (charKind == UCK_normal) || (charKind == UCK_quote) ) continue; + if ( (charKind == UCK_comma) && preserveCommas ) continue; + if ( charKind != UCK_space ) break; + + if ( (itemEnd + charSize) >= endPos ) break; // Anything left? + ClassifyCharacter ( catedStr, (itemEnd+charSize), &nextKind, &nextSize, &nextChar ); + if ( (nextKind == UCK_normal) || (nextKind == UCK_quote) ) continue; + if ( (nextKind == UCK_comma) && preserveCommas ) continue; + break; // Have multiple spaces, or a space followed by a separator. + + } + + itemValue.assign ( catedStr, itemStart, (itemEnd - itemStart) ); + + } else { + + // Accumulate quoted values into a local string, undoubling internal quotes that + // match the surrounding quotes. Do not undouble "unmatching" quotes. + + UniCodePoint openQuote = uniChar; + UniCodePoint closeQuote = GetClosingQuote ( openQuote ); + + itemStart += charSize; // Skip the opening quote; + itemValue.erase(); + + for ( itemEnd = itemStart; itemEnd < endPos; itemEnd += charSize ) { + + ClassifyCharacter ( catedStr, itemEnd, &charKind, &charSize, &uniChar ); + + if ( (charKind != UCK_quote) || (! IsSurroundingQuote ( uniChar, openQuote, closeQuote)) ) { + + // This is not a matching quote, just append it to the item value. + itemValue.append ( catedStr, itemEnd, charSize ); + + } else { + + // This is a "matching" quote. Is it doubled, or the final closing quote? Tolerate + // various edge cases like undoubled opening (non-closing) quotes, or end of input. + + if ( (itemEnd + charSize) < endPos ) { + ClassifyCharacter ( catedStr, itemEnd+charSize, &nextKind, &nextSize, &nextChar ); + } else { + nextKind = UCK_semicolon; nextSize = 0; nextChar = 0x3B; + } + + if ( uniChar == nextChar ) { + // This is doubled, copy it and skip the double. + itemValue.append ( catedStr, itemEnd, charSize ); + itemEnd += nextSize; // Loop will add in charSize. + } else if ( ! IsClosingingQuote ( uniChar, openQuote, closeQuote ) ) { + // This is an undoubled, non-closing quote, copy it. + itemValue.append ( catedStr, itemEnd, charSize ); + } else { + // This is an undoubled closing quote, skip it and exit the loop. + itemEnd += charSize; + break; + } + + } + + } // Loop to accumulate the quoted value. + + } + + // Add the separated item to the array. Keep a matching old value in case it had separators. + + size_t oldChild; + for ( oldChild = 0; oldChild < oldChildCount; ++oldChild ) { + if ( (oldChildren[oldChild] != 0) && (itemValue == oldChildren[oldChild]->value) ) break; + } + + XMP_Node * newItem = 0; + if ( oldChild == oldChildCount ) { + newItem = new XMP_Node ( arrayNode, kXMP_ArrayItemName, itemValue.c_str(), 0 ); + } else { + newItem = oldChildren[oldChild]; + oldChildren[oldChild] = 0; // ! Don't match again, let duplicates be seen. + } + arrayNode->children.push_back ( newItem ); + + } // Loop through all of the returned items. + + // Delete any of the old children that were not kept. + for ( size_t i = 0; i < oldChildCount; ++i ) { + if ( oldChildren[i] != 0 ) delete oldChildren[i]; + } + +} // SeparateArrayItems + + +// ------------------------------------------------------------------------------------------------- +// ApplyTemplate +// ------------- + +/* class static */ void +XMPUtils::ApplyTemplate ( XMPMeta * workingXMP, + const XMPMeta & templateXMP, + XMP_OptionBits actions ) +{ + bool doClear = XMP_OptionIsSet ( actions, kXMPTemplate_ClearUnnamedProperties ); + bool doAdd = XMP_OptionIsSet ( actions, kXMPTemplate_AddNewProperties ); + bool doReplace = XMP_OptionIsSet ( actions, kXMPTemplate_ReplaceExistingProperties ); + + bool deleteEmpty = XMP_OptionIsSet ( actions, kXMPTemplate_ReplaceWithDeleteEmpty ); + doReplace |= deleteEmpty; // Delete-empty implies Replace. + deleteEmpty &= (! doClear); // Clear implies not delete-empty, but keep the implicit Replace. + + bool doAll = XMP_OptionIsSet ( actions, kXMPTemplate_IncludeInternalProperties ); + + // ! In several places we do loops backwards so that deletions do not perturb the remaining indices. + // ! These loops use ordinals (size .. 1), we must use a zero based index inside the loop. + + if ( doClear ) { + + // Visit the top level working properties, delete if not in the template. + + for ( size_t schemaOrdinal = workingXMP->tree.children.size(); schemaOrdinal > 0; --schemaOrdinal ) { + + size_t schemaNum = schemaOrdinal-1; // ! Convert ordinal to index! + XMP_Node * workingSchema = workingXMP->tree.children[schemaNum]; + const XMP_Node * templateSchema = FindConstSchema ( &templateXMP.tree, workingSchema->name.c_str() ); + + if ( templateSchema == 0 ) { + + // The schema is not in the template, delete all properties or just all external ones. + + if ( doAll ) { + + workingSchema->RemoveChildren(); // Remove the properties here, delete the schema below. + + } else { + + for ( size_t propOrdinal = workingSchema->children.size(); propOrdinal > 0; --propOrdinal ) { + size_t propNum = propOrdinal-1; // ! Convert ordinal to index! + XMP_Node * workingProp = workingSchema->children[propNum]; + if ( IsExternalProperty ( workingSchema->name, workingProp->name ) ) { + delete ( workingProp ); + workingSchema->children.erase ( workingSchema->children.begin() + propNum ); + } + } + + } + + } else { + + // Check each of the working XMP's properties to see if it is in the template. + + for ( size_t propOrdinal = workingSchema->children.size(); propOrdinal > 0; --propOrdinal ) { + size_t propNum = propOrdinal-1; // ! Convert ordinal to index! + XMP_Node * workingProp = workingSchema->children[propNum]; + if ( (doAll || IsExternalProperty ( workingSchema->name, workingProp->name )) && + (FindConstChild ( templateSchema, workingProp->name.c_str() ) == 0) ) { + delete ( workingProp ); + workingSchema->children.erase ( workingSchema->children.begin() + propNum ); + } + } + + } + + if ( workingSchema->children.empty() ) { + delete ( workingSchema ); + workingXMP->tree.children.erase ( workingXMP->tree.children.begin() + schemaNum ); + } + + } + + } + + if ( doAdd | doReplace ) { + + for ( size_t schemaNum = 0, schemaLim = templateXMP.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) { + + const XMP_Node * templateSchema = templateXMP.tree.children[schemaNum]; + + // Make sure we have an output schema node, then process the top level template properties. + + XMP_NodePtrPos workingSchemaPos; + XMP_Node * workingSchema = FindSchemaNode ( &workingXMP->tree, templateSchema->name.c_str(), + kXMP_ExistingOnly, &workingSchemaPos ); + if ( workingSchema == 0 ) { + workingSchema = new XMP_Node ( &workingXMP->tree, templateSchema->name, templateSchema->value, kXMP_SchemaNode ); + workingXMP->tree.children.push_back ( workingSchema ); + workingSchemaPos = workingXMP->tree.children.end() - 1; + } + + for ( size_t propNum = 0, propLim = templateSchema->children.size(); propNum < propLim; ++propNum ) { + const XMP_Node * templateProp = templateSchema->children[propNum]; + if ( doAll || IsExternalProperty ( templateSchema->name, templateProp->name ) ) { + AppendSubtree ( templateProp, workingSchema, doAdd, doReplace, deleteEmpty ); + } + } + + if ( workingSchema->children.empty() ) { + delete ( workingSchema ); + workingXMP->tree.children.erase ( workingSchemaPos ); + } + + } + + } + +} // ApplyTemplate + + +// ------------------------------------------------------------------------------------------------- +// RemoveProperties +// ---------------- + +/* class static */ void +XMPUtils::RemoveProperties ( XMPMeta * xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options ) +{ + XMP_Assert ( (schemaNS != 0) && (propName != 0) ); // ! Enforced by wrapper. + + const bool doAll = XMP_TestOption (options, kXMPUtil_DoAllProperties ); + const bool includeAliases = XMP_TestOption ( options, kXMPUtil_IncludeAliases ); + + if ( *propName != 0 ) { + + // Remove just the one indicated property. This might be an alias, the named schema might + // not actually exist. So don't lookup the schema node. + + if ( *schemaNS == 0 ) XMP_Throw ( "Property name requires schema namespace", kXMPErr_BadParam ); + + XMP_ExpandedXPath expPath; + ExpandXPath ( schemaNS, propName, &expPath ); + + XMP_NodePtrPos propPos; + XMP_Node * propNode = FindNode ( &(xmpObj->tree), expPath, kXMP_ExistingOnly, kXMP_NoOptions, &propPos ); + if ( propNode != 0 ) { + if ( doAll || IsExternalProperty ( expPath[kSchemaStep].step, expPath[kRootPropStep].step ) ) { + XMP_Node * parent = propNode->parent; // *** Should have XMP_Node::RemoveChild(pos). + delete propNode; // ! Both delete the node and erase the pointer from the parent. + parent->children.erase ( propPos ); + DeleteEmptySchema ( parent ); + } + } + + } else if ( *schemaNS != 0 ) { + + // Remove all properties from the named schema. Optionally include aliases, in which case + // there might not be an actual schema node. + + XMP_NodePtrPos schemaPos; + XMP_Node * schemaNode = FindSchemaNode ( &xmpObj->tree, schemaNS, kXMP_ExistingOnly, &schemaPos ); + if ( schemaNode != 0 ) RemoveSchemaChildren ( schemaPos, doAll ); + + if ( includeAliases ) { + + // We're removing the aliases also. Look them up by their namespace prefix. Yes, the + // alias map is sorted so we could process just that portion. But that takes more code + // and the extra speed isn't worth it. (Plus this way we avoid a dependence on the map + // implementation.) Lookup the XMP node from the alias, to make sure the actual exists. + + XMP_StringPtr nsPrefix; + XMP_StringLen nsLen; + (void) XMPMeta::GetNamespacePrefix ( schemaNS, &nsPrefix, &nsLen ); + + XMP_AliasMapPos currAlias = sRegisteredAliasMap->begin(); + XMP_AliasMapPos endAlias = sRegisteredAliasMap->end(); + + for ( ; currAlias != endAlias; ++currAlias ) { + if ( strncmp ( currAlias->first.c_str(), nsPrefix, nsLen ) == 0 ) { + XMP_NodePtrPos actualPos; + XMP_Node * actualProp = FindNode ( &xmpObj->tree, currAlias->second, kXMP_ExistingOnly, kXMP_NoOptions, &actualPos ); + if ( actualProp != 0 ) { + XMP_Node * rootProp = actualProp; + while ( ! XMP_NodeIsSchema ( rootProp->parent->options ) ) rootProp = rootProp->parent; + if ( doAll || IsExternalProperty ( rootProp->parent->name, rootProp->name ) ) { + XMP_Node * parent = actualProp->parent; + delete actualProp; // ! Both delete the node and erase the pointer from the parent. + parent->children.erase ( actualPos ); + DeleteEmptySchema ( parent ); + } + } + } + } + + } + + } else { + + // Remove all appropriate properties from all schema. In this case we don't have to be + // concerned with aliases, they are handled implicitly from the actual properties. + + // ! Iterate backwards to reduce shuffling if schema are erased and to simplify the logic + // ! for denoting the current schema. (Erasing schema n makes the old n+1 now be n.) + + size_t schemaCount = xmpObj->tree.children.size(); + XMP_NodePtrPos beginPos = xmpObj->tree.children.begin(); + + for ( size_t schemaNum = schemaCount-1, schemaLim = (size_t)(-1); schemaNum != schemaLim; --schemaNum ) { + XMP_NodePtrPos currSchema = beginPos + schemaNum; + RemoveSchemaChildren ( currSchema, doAll ); + } + + } + +} // RemoveProperties + + +// ------------------------------------------------------------------------------------------------- +// DuplicateSubtree +// ---------------- + +/* class static */ void +XMPUtils::DuplicateSubtree ( const XMPMeta & source, + XMPMeta * dest, + XMP_StringPtr sourceNS, + XMP_StringPtr sourceRoot, + XMP_StringPtr destNS, + XMP_StringPtr destRoot, + XMP_OptionBits options ) +{ + IgnoreParam(options); + + bool fullSourceTree = false; + bool fullDestTree = false; + + XMP_ExpandedXPath sourcePath, destPath; + + const XMP_Node * sourceNode = 0; + XMP_Node * destNode = 0; + + XMP_Assert ( (sourceNS != 0) && (*sourceNS != 0) ); + XMP_Assert ( (sourceRoot != 0) && (*sourceRoot != 0) ); + XMP_Assert ( (dest != 0) && (destNS != 0) && (destRoot != 0) ); + + if ( *destNS == 0 ) destNS = sourceNS; + if ( *destRoot == 0 ) destRoot = sourceRoot; + + if ( XMP_LitMatch ( sourceNS, "*" ) ) fullSourceTree = true; + if ( XMP_LitMatch ( destNS, "*" ) ) fullDestTree = true; + + if ( (&source == dest) && (fullSourceTree | fullDestTree) ) { + XMP_Throw ( "Can't duplicate tree onto itself", kXMPErr_BadParam ); + } + + if ( fullSourceTree & fullDestTree ) XMP_Throw ( "Use Clone for full tree to full tree", kXMPErr_BadParam ); + + if ( fullSourceTree ) { + + // The destination must be an existing empty struct, copy all of the source top level as fields. + + ExpandXPath ( destNS, destRoot, &destPath ); + destNode = FindNode ( &dest->tree, destPath, kXMP_ExistingOnly ); + + if ( (destNode == 0) || (! XMP_PropIsStruct ( destNode->options )) ) { + XMP_Throw ( "Destination must be an existing struct", kXMPErr_BadXPath ); + } + + if ( ! destNode->children.empty() ) { + if ( options & kXMP_DeleteExisting ) { + destNode->RemoveChildren(); + } else { + XMP_Throw ( "Destination must be an empty struct", kXMPErr_BadXPath ); + } + } + + for ( size_t schemaNum = 0, schemaLim = source.tree.children.size(); schemaNum < schemaLim; ++schemaNum ) { + + const XMP_Node * currSchema = source.tree.children[schemaNum]; + + for ( size_t propNum = 0, propLim = currSchema->children.size(); propNum < propLim; ++propNum ) { + sourceNode = currSchema->children[propNum]; + XMP_Node * copyNode = new XMP_Node ( destNode, sourceNode->name, sourceNode->value, sourceNode->options ); + destNode->children.push_back ( copyNode ); + CloneOffspring ( sourceNode, copyNode ); + } + + } + + } else if ( fullDestTree ) { + + // The source node must be an existing struct, copy all of the fields to the dest top level. + + XMP_ExpandedXPath srcPath; + ExpandXPath ( sourceNS, sourceRoot, &srcPath ); + sourceNode = FindConstNode ( &source.tree, srcPath ); + + if ( (sourceNode == 0) || (! XMP_PropIsStruct ( sourceNode->options )) ) { + XMP_Throw ( "Source must be an existing struct", kXMPErr_BadXPath ); + } + + destNode = &dest->tree; + + if ( ! destNode->children.empty() ) { + if ( options & kXMP_DeleteExisting ) { + destNode->RemoveChildren(); + } else { + XMP_Throw ( "Destination tree must be empty", kXMPErr_BadXPath ); + } + } + + std::string nsPrefix; + XMP_StringPtr nsURI; + XMP_StringLen nsLen; + + for ( size_t fieldNum = 0, fieldLim = sourceNode->children.size(); fieldNum < fieldLim; ++fieldNum ) { + + const XMP_Node * currField = sourceNode->children[fieldNum]; + + size_t colonPos = currField->name.find ( ':' ); + if ( colonPos == std::string::npos ) continue; + nsPrefix.assign ( currField->name.c_str(), colonPos ); + bool nsOK = XMPMeta::GetNamespaceURI ( nsPrefix.c_str(), &nsURI, &nsLen ); + if ( ! nsOK ) XMP_Throw ( "Source field namespace is not global", kXMPErr_BadSchema ); + + XMP_Node * destSchema = FindSchemaNode ( &dest->tree, nsURI, kXMP_CreateNodes ); + if ( destSchema == 0 ) XMP_Throw ( "Failed to find destination schema", kXMPErr_BadSchema ); + + XMP_Node * copyNode = new XMP_Node ( destSchema, currField->name, currField->value, currField->options ); + destSchema->children.push_back ( copyNode ); + CloneOffspring ( currField, copyNode ); + + } + + } else { + + // Find the root nodes for the source and destination subtrees. + + ExpandXPath ( sourceNS, sourceRoot, &sourcePath ); + ExpandXPath ( destNS, destRoot, &destPath ); + + sourceNode = FindConstNode ( &source.tree, sourcePath ); + if ( sourceNode == 0 ) XMP_Throw ( "Can't find source subtree", kXMPErr_BadXPath ); + + destNode = FindNode ( &dest->tree, destPath, kXMP_ExistingOnly ); // Dest must not yet exist. + if ( destNode != 0 ) XMP_Throw ( "Destination subtree must not exist", kXMPErr_BadXPath ); + + destNode = FindNode ( &dest->tree, destPath, kXMP_CreateNodes ); // Now create the dest. + if ( destNode == 0 ) XMP_Throw ( "Can't create destination root node", kXMPErr_BadXPath ); + + // Make sure the destination is not within the source! The source can't be inside the destination + // because the source already existed and the destination was just created. + + if ( &source == dest ) { + for ( XMP_Node * testNode = destNode; testNode != 0; testNode = testNode->parent ) { + if ( testNode == sourceNode ) { + // *** delete the just-created dest root node + XMP_Throw ( "Destination subtree is within the source subtree", kXMPErr_BadXPath ); + } + } + } + + // *** Could use a CloneTree util here and maybe elsewhere. + + destNode->value = sourceNode->value; // *** Should use SetNode. + destNode->options = sourceNode->options; + CloneOffspring ( sourceNode, destNode ); + + } + +} // DuplicateSubtree + + +// ================================================================================================= diff --git a/source/lib/xmp_core/XMPUtils.cpp b/source/lib/xmp_core/XMPUtils.cpp new file mode 100644 index 0000000..af65dc9 --- /dev/null +++ b/source/lib/xmp_core/XMPUtils.cpp @@ -0,0 +1,2000 @@ +// ================================================================================================= +// Copyright 2003 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! This must be the first include! +#include "XMPCore_Impl.hpp" + +#include "XMPUtils.hpp" + +#include "md5.h" + +#include + +#include +#include +#include +#include +#include + +#include // For snprintf. + +#if XMP_WinBuild + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) + #pragma warning ( disable : 4996 ) // '...' was declared deprecated +#endif + +// ================================================================================================= +// Local Types and Constants +// ========================= + +static const char * sBase64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; + +// ================================================================================================= +// Local Utilities +// =============== + +// ------------------------------------------------------------------------------------------------- +// ANSI Time Functions +// ------------------- +// +// A bit of hackery to use the best available time functions. Mac, UNIX and iOS have thread safe versions +// of gmtime and localtime. + +#if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild + + typedef time_t ansi_tt; + typedef struct tm ansi_tm; + + #define ansi_time time + #define ansi_mktime mktime + #define ansi_difftime difftime + + #define ansi_gmtime gmtime_r + #define ansi_localtime localtime_r + +#elif XMP_WinBuild + + // ! VS.Net 2003 (VC7) does not provide thread safe versions of gmtime and localtime. + // ! VS.Net 2005 (VC8) inverts the parameters for the safe versions of gmtime and localtime. + + typedef time_t ansi_tt; + typedef struct tm ansi_tm; + + #define ansi_time time + #define ansi_mktime mktime + #define ansi_difftime difftime + + #if defined(_MSC_VER) && (_MSC_VER >= 1400) + #define ansi_gmtime(tt,tm) gmtime_s ( tm, tt ) + #define ansi_localtime(tt,tm) localtime_s ( tm, tt ) + #else + static inline void ansi_gmtime ( const ansi_tt * ttTime, ansi_tm * tmTime ) + { + ansi_tm * tmx = gmtime ( ttTime ); // ! Hope that there is no race! + if ( tmx == 0 ) XMP_Throw ( "Failure from ANSI C gmtime function", kXMPErr_ExternalFailure ); + *tmTime = *tmx; + } + static inline void ansi_localtime ( const ansi_tt * ttTime, ansi_tm * tmTime ) + { + ansi_tm * tmx = localtime ( ttTime ); // ! Hope that there is no race! + if ( tmx == 0 ) XMP_Throw ( "Failure from ANSI C localtime function", kXMPErr_ExternalFailure ); + *tmTime = *tmx; + } + #endif + +#endif + +// ------------------------------------------------------------------------------------------------- +// VerifyDateTimeFlags +// ------------------- + +static void +VerifyDateTimeFlags ( XMP_DateTime * dt ) +{ + + if ( (dt->year != 0) || (dt->month != 0) || (dt->day != 0) ) dt->hasDate = true; + if ( (dt->hour != 0) || (dt->minute != 0) || (dt->second != 0) || (dt->nanoSecond != 0) ) dt->hasTime = true; + if ( (dt->tzSign != 0) || (dt->tzHour != 0) || (dt->tzMinute != 0) ) dt->hasTimeZone = true; + if ( dt->hasTimeZone ) dt->hasTime = true; // ! Don't combine with above line, UTC has zero values. + +} // VerifyDateTimeFlags + +// ------------------------------------------------------------------------------------------------- +// IsLeapYear +// ---------- + +static bool +IsLeapYear ( long year ) +{ + + if ( year < 0 ) year = -year + 1; // Fold the negative years, assuming there is a year 0. + + if ( (year % 4) != 0 ) return false; // Not a multiple of 4. + if ( (year % 100) != 0 ) return true; // A multiple of 4 but not a multiple of 100. + if ( (year % 400) == 0 ) return true; // A multiple of 400. + + return false; // A multiple of 100 but not a multiple of 400. + +} // IsLeapYear + +// ------------------------------------------------------------------------------------------------- +// DaysInMonth +// ----------- + +static int +DaysInMonth ( XMP_Int32 year, XMP_Int32 month ) +{ + + static short daysInMonth[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + // Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec + + int days = daysInMonth [ month ]; + if ( (month == 2) && IsLeapYear ( year ) ) days += 1; + + return days; + +} // DaysInMonth + +// ------------------------------------------------------------------------------------------------- +// AdjustTimeOverflow +// ------------------ + +static void +AdjustTimeOverflow ( XMP_DateTime * time ) +{ + enum { kBillion = 1000*1000*1000L }; + + // ---------------------------------------------------------------------------------------------- + // To be safe against pathalogical overflow we first adjust from month to second, then from + // nanosecond back up to month. This leaves each value closer to zero before propagating into it. + // For example if the hour and minute are both near max, adjusting minutes first can cause the + // hour to overflow. + + // ! Photoshop 8 creates "time only" values with zeros for year, month, and day. + + if ( (time->year != 0) || (time->month != 0) || (time->day != 0) ) { + + while ( time->month < 1 ) { + time->year -= 1; + time->month += 12; + } + + while ( time->month > 12 ) { + time->year += 1; + time->month -= 12; + } + + while ( time->day < 1 ) { + time->month -= 1; + if ( time->month < 1 ) { // ! Keep the months in range for indexing daysInMonth! + time->year -= 1; + time->month += 12; + } + time->day += DaysInMonth ( time->year, time->month ); // ! Decrement month before so index here is right! + } + + while ( time->day > DaysInMonth ( time->year, time->month ) ) { + time->day -= DaysInMonth ( time->year, time->month ); // ! Increment month after so index here is right! + time->month += 1; + if ( time->month > 12 ) { + time->year += 1; + time->month -= 12; + } + } + + } + + while ( time->hour < 0 ) { + time->day -= 1; + time->hour += 24; + } + + while ( time->hour >= 24 ) { + time->day += 1; + time->hour -= 24; + } + + while ( time->minute < 0 ) { + time->hour -= 1; + time->minute += 60; + } + + while ( time->minute >= 60 ) { + time->hour += 1; + time->minute -= 60; + } + + while ( time->second < 0 ) { + time->minute -= 1; + time->second += 60; + } + + while ( time->second >= 60 ) { + time->minute += 1; + time->second -= 60; + } + + while ( time->nanoSecond < 0 ) { + time->second -= 1; + time->nanoSecond += kBillion; + } + + while ( time->nanoSecond >= kBillion ) { + time->second += 1; + time->nanoSecond -= kBillion; + } + + while ( time->second < 0 ) { + time->minute -= 1; + time->second += 60; + } + + while ( time->second >= 60 ) { + time->minute += 1; + time->second -= 60; + } + + while ( time->minute < 0 ) { + time->hour -= 1; + time->minute += 60; + } + + while ( time->minute >= 60 ) { + time->hour += 1; + time->minute -= 60; + } + + while ( time->hour < 0 ) { + time->day -= 1; + time->hour += 24; + } + + while ( time->hour >= 24 ) { + time->day += 1; + time->hour -= 24; + } + + if ( (time->year != 0) || (time->month != 0) || (time->day != 0) ) { + + while ( time->month < 1 ) { // Make sure the months are OK first, for DaysInMonth. + time->year -= 1; + time->month += 12; + } + + while ( time->month > 12 ) { + time->year += 1; + time->month -= 12; + } + + while ( time->day < 1 ) { + time->month -= 1; + if ( time->month < 1 ) { + time->year -= 1; + time->month += 12; + } + time->day += DaysInMonth ( time->year, time->month ); + } + + while ( time->day > DaysInMonth ( time->year, time->month ) ) { + time->day -= DaysInMonth ( time->year, time->month ); + time->month += 1; + if ( time->month > 12 ) { + time->year += 1; + time->month -= 12; + } + } + + } + +} // AdjustTimeOverflow + +// ------------------------------------------------------------------------------------------------- +// GatherInt +// --------- +// +// Gather into a 64-bit value in order to easily check for overflow. Using a 32-bit value and +// checking for negative isn't reliable, the "*10" part can wrap around to a low positive value. + +static XMP_Int32 +GatherInt ( XMP_StringPtr strValue, size_t * _pos, const char * errMsg ) +{ + size_t pos = *_pos; + XMP_Int64 value = 0; + + enum { kMaxSInt32 = 0x7FFFFFFF }; + + for ( char ch = strValue[pos]; ('0' <= ch) && (ch <= '9'); ++pos, ch = strValue[pos] ) { + value = (value * 10) + (ch - '0'); + if ( value > kMaxSInt32 ) XMP_Throw ( errMsg, kXMPErr_BadValue ); + } + + if ( pos == *_pos ) XMP_Throw ( errMsg, kXMPErr_BadParam ); + *_pos = pos; + return (XMP_Int32)value; + +} // GatherInt + +// ------------------------------------------------------------------------------------------------- + +static void FormatFullDateTime ( XMP_DateTime & tempDate, char * buffer, size_t bufferLen ) +{ + + AdjustTimeOverflow ( &tempDate ); // Make sure all time parts are in range. + + if ( (tempDate.second == 0) && (tempDate.nanoSecond == 0) ) { + + // Output YYYY-MM-DDThh:mmTZD. + snprintf ( buffer, bufferLen, "%.4d-%02d-%02dT%02d:%02d", // AUDIT: Callers pass sizeof(buffer). + tempDate.year, tempDate.month, tempDate.day, tempDate.hour, tempDate.minute ); + + } else if ( tempDate.nanoSecond == 0 ) { + + // Output YYYY-MM-DDThh:mm:ssTZD. + snprintf ( buffer, bufferLen, "%.4d-%02d-%02dT%02d:%02d:%02d", // AUDIT: Callers pass sizeof(buffer). + tempDate.year, tempDate.month, tempDate.day, + tempDate.hour, tempDate.minute, tempDate.second ); + + } else { + + // Output YYYY-MM-DDThh:mm:ss.sTZD. + snprintf ( buffer, bufferLen, "%.4d-%02d-%02dT%02d:%02d:%02d.%09d", // AUDIT: Callers pass sizeof(buffer). + tempDate.year, tempDate.month, tempDate.day, + tempDate.hour, tempDate.minute, tempDate.second, tempDate.nanoSecond ); + buffer[bufferLen - 1] = 0; // AUDIT warning C6053: make sure string is terminated. buffer is already filled with 0 from caller + for ( size_t i = strlen(buffer)-1; buffer[i] == '0'; --i ) buffer[i] = 0; // Trim excess digits. + } + +} // FormatFullDateTime + +// ------------------------------------------------------------------------------------------------- +// DecodeBase64Char +// ---------------- + +// The decode mapping: +// +// encoded encoded raw +// char value value +// ------- ------- ----- +// A .. Z 0x41 .. 0x5A 0 .. 25 +// a .. z 0x61 .. 0x7A 26 .. 51 +// 0 .. 9 0x30 .. 0x39 52 .. 61 +// + 0x2B 62 +// / 0x2F 63 + +static unsigned char +DecodeBase64Char ( XMP_Uns8 ch ) +{ + + if ( ('A' <= ch) && (ch <= 'Z') ) { + ch = ch - 'A'; + } else if ( ('a' <= ch) && (ch <= 'z') ) { + ch = ch - 'a' + 26; + } else if ( ('0' <= ch) && (ch <= '9') ) { + ch = ch - '0' + 52; + } else if ( ch == '+' ) { + ch = 62; + } else if ( ch == '/' ) { + ch = 63; + } else if ( (ch == ' ') || (ch == kTab) || (ch == kLF) || (ch == kCR) ) { + ch = 0xFF; // Will be ignored by the caller. + } else { + XMP_Throw ( "Invalid base-64 encoded character", kXMPErr_BadParam ); + } + + return ch; + +} // DecodeBase64Char (); + +// ------------------------------------------------------------------------------------------------- +// EstimateSizeForJPEG +// ------------------- +// +// Estimate the serialized size for the subtree of an XMP_Node. Support for PackageForJPEG. + +static size_t +EstimateSizeForJPEG ( const XMP_Node * xmpNode ) +{ + + size_t estSize = 0; + size_t nameSize = xmpNode->name.size(); + bool includeName = (! XMP_PropIsArray ( xmpNode->parent->options )); + + if ( XMP_PropIsSimple ( xmpNode->options ) ) { + + if ( includeName ) estSize += (nameSize + 3); // Assume attribute form. + estSize += xmpNode->value.size(); + + } else if ( XMP_PropIsArray ( xmpNode->options ) ) { + + // The form of the value portion is: ...... + if ( includeName ) estSize += (2*nameSize + 5); + size_t arraySize = xmpNode->children.size(); + estSize += 9 + 10; // The rdf:Xyz tags. + estSize += arraySize * (8 + 9); // The rdf:li tags. + for ( size_t i = 0; i < arraySize; ++i ) { + estSize += EstimateSizeForJPEG ( xmpNode->children[i] ); + } + + } else { + + // The form is: ...fields... + if ( includeName ) estSize += (2*nameSize + 5); + estSize += 25; // The rdf:parseType="Resource" attribute. + size_t fieldCount = xmpNode->children.size(); + for ( size_t i = 0; i < fieldCount; ++i ) { + estSize += EstimateSizeForJPEG ( xmpNode->children[i] ); + } + + } + + return estSize; + +} // EstimateSizeForJPEG + +// ------------------------------------------------------------------------------------------------- +// MoveOneProperty +// --------------- + +static bool MoveOneProperty ( XMPMeta & stdXMP, XMPMeta * extXMP, + XMP_StringPtr schemaURI, XMP_StringPtr propName ) +{ + + XMP_Node * propNode = 0; + XMP_NodePtrPos stdPropPos; + + XMP_Node * stdSchema = FindSchemaNode ( &stdXMP.tree, schemaURI, kXMP_ExistingOnly, 0 ); + if ( stdSchema != 0 ) { + propNode = FindChildNode ( stdSchema, propName, kXMP_ExistingOnly, &stdPropPos ); + } + if ( propNode == 0 ) return false; + + XMP_Node * extSchema = FindSchemaNode ( &extXMP->tree, schemaURI, kXMP_CreateNodes ); + + propNode->parent = extSchema; + + extSchema->options &= ~kXMP_NewImplicitNode; + extSchema->children.push_back ( propNode ); + + stdSchema->children.erase ( stdPropPos ); + DeleteEmptySchema ( stdSchema ); + + return true; + +} // MoveOneProperty + +// ------------------------------------------------------------------------------------------------- +// CreateEstimatedSizeMap +// ---------------------- + +#ifndef Trace_PackageForJPEG + #define Trace_PackageForJPEG 0 +#endif + +typedef std::pair < XMP_VarString*, XMP_VarString* > StringPtrPair; +typedef std::multimap < size_t, StringPtrPair > PropSizeMap; + +static void CreateEstimatedSizeMap ( XMPMeta & stdXMP, PropSizeMap * propSizes ) +{ + #if Trace_PackageForJPEG + printf ( " Creating top level property map:\n" ); + #endif + + for ( size_t s = stdXMP.tree.children.size(); s > 0; --s ) { + + XMP_Node * stdSchema = stdXMP.tree.children[s-1]; + + for ( size_t p = stdSchema->children.size(); p > 0; --p ) { + + XMP_Node * stdProp = stdSchema->children[p-1]; + if ( (stdSchema->name == kXMP_NS_XMP_Note) && + (stdProp->name == "xmpNote:HasExtendedXMP") ) continue; // ! Don't move xmpNote:HasExtendedXMP. + + size_t propSize = EstimateSizeForJPEG ( stdProp ); + StringPtrPair namePair ( &stdSchema->name, &stdProp->name ); + PropSizeMap::value_type mapValue ( propSize, namePair ); + + (void) propSizes->insert ( propSizes->upper_bound ( propSize ), mapValue ); + #if Trace_PackageForJPEG + printf ( " %d bytes, %s in %s\n", propSize, stdProp->name.c_str(), stdSchema->name.c_str() ); + #endif + + } + + } + +} // CreateEstimatedSizeMap + +// ------------------------------------------------------------------------------------------------- +// MoveLargestProperty +// ------------------- + +static size_t MoveLargestProperty ( XMPMeta & stdXMP, XMPMeta * extXMP, PropSizeMap & propSizes ) +{ + XMP_Assert ( ! propSizes.empty() ); + + #if 0 + // *** Xocde 2.3 on Mac OS X 10.4.7 seems to have a bug where this does not pick the last + // *** item in the map. We'll just avoid it on all platforms until thoroughly tested. + PropSizeMap::iterator lastPos = propSizes.end(); + --lastPos; // Move to the actual last item. + #else + PropSizeMap::iterator lastPos = propSizes.begin(); + PropSizeMap::iterator nextPos = lastPos; + for ( ++nextPos; nextPos != propSizes.end(); ++nextPos ) lastPos = nextPos; + #endif + + size_t propSize = lastPos->first; + const char * schemaURI = lastPos->second.first->c_str(); + const char * propName = lastPos->second.second->c_str(); + + #if Trace_PackageForJPEG + printf ( " Move %s, %d bytes\n", propName, propSize ); + #endif + + bool moved = MoveOneProperty ( stdXMP, extXMP, schemaURI, propName ); + XMP_Assert ( moved ); + + propSizes.erase ( lastPos ); + return propSize; + +} // MoveLargestProperty + +// ================================================================================================= +// Class Static Functions +// ====================== + +// ------------------------------------------------------------------------------------------------- +// Initialize +// ---------- + +/* class static */ bool +XMPUtils::Initialize() +{ + + if ( WhiteSpaceStrPtr == NULL ) { + WhiteSpaceStrPtr = new std::string(); + WhiteSpaceStrPtr->append( " \t\n\r" ); + } + return true; + +} // Initialize + +// ------------------------------------------------------------------------------------------------- +// Terminate +// --------- + +/* class static */ void +XMPUtils::Terminate() RELEASE_NO_THROW +{ + + delete WhiteSpaceStrPtr; + WhiteSpaceStrPtr = NULL; + return; + +} // Terminate + +// ------------------------------------------------------------------------------------------------- +// ComposeArrayItemPath +// -------------------- +// +// Return "arrayName[index]". + +/* class static */ void +XMPUtils::ComposeArrayItemPath ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_VarString * _fullPath ) +{ + XMP_Assert ( schemaNS != 0 ); // Enforced by wrapper. + XMP_Assert ( (arrayName != 0) && (*arrayName != 0) ); // Enforced by wrapper. + XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper. + + XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path. + ExpandXPath ( schemaNS, arrayName, &expPath ); + + if ( (itemIndex < 0) && (itemIndex != kXMP_ArrayLastItem) ) XMP_Throw ( "Array index out of bounds", kXMPErr_BadParam ); + + XMP_StringLen reserveLen = strlen(arrayName) + 2 + 32; // Room plus padding. + + XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str(). + fullPath.reserve ( reserveLen ); + fullPath = arrayName; + + if ( itemIndex == kXMP_ArrayLastItem ) { + fullPath += "[last()]"; + } else { + // AUDIT: Using sizeof(buffer) for the snprintf length is safe. + char buffer [32]; // A 32 byte buffer is plenty, even for a 64-bit integer. + snprintf ( buffer, sizeof(buffer), "[%d]", itemIndex ); + fullPath += buffer; + } + + *_fullPath = fullPath; + +} // ComposeArrayItemPath + +// ------------------------------------------------------------------------------------------------- +// ComposeStructFieldPath +// ---------------------- +// +// Return "structName/ns:fieldName". + +/* class static */ void +XMPUtils::ComposeStructFieldPath ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_VarString * _fullPath ) +{ + XMP_Assert ( (schemaNS != 0) && (fieldNS != 0) ); // Enforced by wrapper. + XMP_Assert ( (structName != 0) && (*structName != 0) ); // Enforced by wrapper. + XMP_Assert ( (fieldName != 0) && (*fieldName != 0) ); // Enforced by wrapper. + XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper. + + XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path. + ExpandXPath ( schemaNS, structName, &expPath ); + + XMP_ExpandedXPath fieldPath; + ExpandXPath ( fieldNS, fieldName, &fieldPath ); + if ( fieldPath.size() != 2 ) XMP_Throw ( "The fieldName must be simple", kXMPErr_BadXPath ); + + XMP_StringLen reserveLen = strlen(structName) + fieldPath[kRootPropStep].step.size() + 1; + + XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str(). + fullPath.reserve ( reserveLen ); + fullPath = structName; + fullPath += '/'; + fullPath += fieldPath[kRootPropStep].step; + + *_fullPath = fullPath; + +} // ComposeStructFieldPath + +// ------------------------------------------------------------------------------------------------- +// ComposeQualifierPath +// -------------------- +// +// Return "propName/?ns:qualName". + +/* class static */ void +XMPUtils::ComposeQualifierPath ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_VarString * _fullPath ) +{ + XMP_Assert ( (schemaNS != 0) && (qualNS != 0) ); // Enforced by wrapper. + XMP_Assert ( (propName != 0) && (*propName != 0) ); // Enforced by wrapper. + XMP_Assert ( (qualName != 0) && (*qualName != 0) ); // Enforced by wrapper. + XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper. + + XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path. + ExpandXPath ( schemaNS, propName, &expPath ); + + XMP_ExpandedXPath qualPath; + ExpandXPath ( qualNS, qualName, &qualPath ); + if ( qualPath.size() != 2 ) XMP_Throw ( "The qualifier name must be simple", kXMPErr_BadXPath ); + + XMP_StringLen reserveLen = strlen(propName) + qualPath[kRootPropStep].step.size() + 2; + + XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str(). + fullPath.reserve ( reserveLen ); + fullPath = propName; + fullPath += "/?"; + fullPath += qualPath[kRootPropStep].step; + + *_fullPath = fullPath; + +} // ComposeQualifierPath + +// ------------------------------------------------------------------------------------------------- +// ComposeLangSelector +// ------------------- +// +// Return "arrayName[?xml:lang="lang"]". + +// *** #error "handle quotes in the lang - or verify format" + +/* class static */ void +XMPUtils::ComposeLangSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr _langName, + XMP_VarString * _fullPath ) +{ + XMP_Assert ( schemaNS != 0 ); // Enforced by wrapper. + XMP_Assert ( (arrayName != 0) && (*arrayName != 0) ); // Enforced by wrapper. + XMP_Assert ( (_langName != 0) && (*_langName != 0) ); // Enforced by wrapper. + XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper. + + XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path. + ExpandXPath ( schemaNS, arrayName, &expPath ); + + XMP_VarString langName ( _langName ); + NormalizeLangValue ( &langName ); + + XMP_StringLen reserveLen = strlen(arrayName) + langName.size() + 14; + + XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str(). + fullPath.reserve ( reserveLen ); + fullPath = arrayName; + fullPath += "[?xml:lang=\""; + fullPath += langName; + fullPath += "\"]"; + + *_fullPath = fullPath; + +} // ComposeLangSelector + +// ------------------------------------------------------------------------------------------------- +// ComposeFieldSelector +// -------------------- +// +// Return "arrayName[ns:fieldName="fieldValue"]". + +// *** #error "handle quotes in the value" + +/* class static */ void +XMPUtils::ComposeFieldSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + XMP_VarString * _fullPath ) +{ + XMP_Assert ( (schemaNS != 0) && (fieldNS != 0) && (fieldValue != 0) ); // Enforced by wrapper. + XMP_Assert ( (*arrayName != 0) && (*fieldName != 0) ); // Enforced by wrapper. + XMP_Assert ( _fullPath != 0 ); // Enforced by wrapper. + + XMP_ExpandedXPath expPath; // Just for side effects to check namespace and basic path. + ExpandXPath ( schemaNS, arrayName, &expPath ); + + XMP_ExpandedXPath fieldPath; + ExpandXPath ( fieldNS, fieldName, &fieldPath ); + if ( fieldPath.size() != 2 ) XMP_Throw ( "The fieldName must be simple", kXMPErr_BadXPath ); + + XMP_StringLen reserveLen = strlen(arrayName) + fieldPath[kRootPropStep].step.size() + strlen(fieldValue) + 5; + + XMP_VarString fullPath; // ! Allow for arrayName to be the incoming _fullPath.c_str(). + fullPath.reserve ( reserveLen ); + fullPath = arrayName; + fullPath += '['; + fullPath += fieldPath[kRootPropStep].step; + fullPath += "=\""; + fullPath += fieldValue; + fullPath += "\"]"; + + *_fullPath = fullPath; + +} // ComposeFieldSelector + +// ------------------------------------------------------------------------------------------------- +// ConvertFromBool +// --------------- + +/* class static */ void +XMPUtils::ConvertFromBool ( bool binValue, + XMP_VarString * strValue ) +{ + XMP_Assert ( strValue != 0 ); // Enforced by wrapper. + + if ( binValue ) { + *strValue = kXMP_TrueStr; + } else { + *strValue = kXMP_FalseStr; + } + +} // ConvertFromBool + +// ------------------------------------------------------------------------------------------------- +// ConvertFromInt +// -------------- + +/* class static */ void +XMPUtils::ConvertFromInt ( XMP_Int32 binValue, + XMP_StringPtr format, + XMP_VarString * strValue ) +{ + XMP_Assert ( (format != 0) && (strValue != 0) ); // Enforced by wrapper. + + strValue->erase(); + if ( *format == 0 ) format = "%d"; + + // AUDIT: Using sizeof(buffer) for the snprintf length is safe. + char buffer [32]; // Big enough for a 64-bit integer; + snprintf ( buffer, sizeof(buffer), format, binValue ); + + *strValue = buffer; + +} // ConvertFromInt + +// ------------------------------------------------------------------------------------------------- +// ConvertFromInt64 +// ---------------- + +/* class static */ void +XMPUtils::ConvertFromInt64 ( XMP_Int64 binValue, + XMP_StringPtr format, + XMP_VarString * strValue ) +{ + XMP_Assert ( (format != 0) && (strValue != 0) ); // Enforced by wrapper. + + strValue->erase(); + if ( *format == 0 ) format = "%lld"; + + // AUDIT: Using sizeof(buffer) for the snprintf length is safe. + char buffer [32]; // Big enough for a 64-bit integer; + snprintf ( buffer, sizeof(buffer), format, binValue ); + + *strValue = buffer; + +} // ConvertFromInt64 + +// ------------------------------------------------------------------------------------------------- +// ConvertFromFloat +// ---------------- + +/* class static */ void +XMPUtils::ConvertFromFloat ( double binValue, + XMP_StringPtr format, + XMP_VarString * strValue ) +{ + XMP_Assert ( (format != 0) && (strValue != 0) ); // Enforced by wrapper. + + strValue->erase(); + if ( *format == 0 ) format = "%f"; + + // AUDIT: Using sizeof(buffer) for the snprintf length is safe. + char buffer [64]; // Ought to be plenty big enough. + snprintf ( buffer, sizeof(buffer), format, binValue ); + + *strValue = buffer; + +} // ConvertFromFloat + +// ------------------------------------------------------------------------------------------------- +// ConvertFromDate +// --------------- +// +// Format a date-time string according to ISO 8601 and http://www.w3.org/TR/NOTE-datetime: +// YYYY +// YYYY-MM +// YYYY-MM-DD +// YYYY-MM-DDThh:mmTZD +// YYYY-MM-DDThh:mm:ssTZD +// YYYY-MM-DDThh:mm:ss.sTZD +// +// YYYY = four-digit year +// MM = two-digit month (01=January, etc.) +// DD = two-digit day of month (01 through 31) +// hh = two digits of hour (00 through 23) +// mm = two digits of minute (00 through 59) +// ss = two digits of second (00 through 59) +// s = one or more digits representing a decimal fraction of a second +// TZD = time zone designator (Z or +hh:mm or -hh:mm) +// +// Note that ISO 8601 does not seem to allow years less than 1000 or greater than 9999. We allow +// any year, even negative ones. The year is formatted as "%.4d". The TZD is also optional in XMP, +// even though required in the W3C profile. Finally, Photoshop 8 (CS) sometimes created time-only +// values so we tolerate that. + +/* class static */ void +XMPUtils::ConvertFromDate ( const XMP_DateTime & _inValue, + XMP_VarString * strValue ) +{ + XMP_Assert ( strValue != 0 ); // Enforced by wrapper. + + char buffer [100]; // Plenty long enough. + memset( buffer, 0, 100); + + // Pick the format, use snprintf to format into a local buffer, assign to static output string. + // Don't use AdjustTimeOverflow at the start, that will wipe out zero month or day values. + + // ! Photoshop 8 creates "time only" values with zeros for year, month, and day. + + XMP_DateTime binValue = _inValue; + VerifyDateTimeFlags ( &binValue ); + + // Temporary fix for bug 1269463, silently fix out of range month or day. + + if ( binValue.month == 0 ) { + if ( (binValue.day != 0) || binValue.hasTime ) binValue.month = 1; + } else { + if ( binValue.month < 1 ) binValue.month = 1; + if ( binValue.month > 12 ) binValue.month = 12; + } + + if ( binValue.day == 0 ) { + if ( binValue.hasTime ) binValue.day = 1; + } else { + if ( binValue.day < 1 ) binValue.day = 1; + if ( binValue.day > 31 ) binValue.day = 31; + } + + // Now carry on with the original logic. + + if ( binValue.month == 0 ) { + + // Output YYYY if all else is zero, otherwise output a full string for the quasi-bogus + // "time only" values from Photoshop CS. + if ( (binValue.day == 0) && (! binValue.hasTime) ) { + snprintf ( buffer, sizeof(buffer), "%.4d", binValue.year ); // AUDIT: Using sizeof for snprintf length is safe. + } else if ( (binValue.year == 0) && (binValue.day == 0) ) { + FormatFullDateTime ( binValue, buffer, sizeof(buffer) ); + } else { + XMP_Throw ( "Invalid partial date", kXMPErr_BadParam); + } + + } else if ( binValue.day == 0 ) { + + // Output YYYY-MM. + if ( (binValue.month < 1) || (binValue.month > 12) ) XMP_Throw ( "Month is out of range", kXMPErr_BadParam); + if ( binValue.hasTime ) XMP_Throw ( "Invalid partial date, non-zeros after zero month and day", kXMPErr_BadParam); + snprintf ( buffer, sizeof(buffer), "%.4d-%02d", binValue.year, binValue.month ); // AUDIT: Using sizeof for snprintf length is safe. + + } else if ( ! binValue.hasTime ) { + + // Output YYYY-MM-DD. + if ( (binValue.month < 1) || (binValue.month > 12) ) XMP_Throw ( "Month is out of range", kXMPErr_BadParam); + if ( (binValue.day < 1) || (binValue.day > 31) ) XMP_Throw ( "Day is out of range", kXMPErr_BadParam); + snprintf ( buffer, sizeof(buffer), "%.4d-%02d-%02d", binValue.year, binValue.month, binValue.day ); // AUDIT: Using sizeof for snprintf length is safe. + + } else { + + FormatFullDateTime ( binValue, buffer, sizeof(buffer) ); + + } + + strValue->assign ( buffer ); + + if ( binValue.hasTimeZone ) { + + if ( (binValue.tzHour < 0) || (binValue.tzHour > 23) || + (binValue.tzMinute < 0 ) || (binValue.tzMinute > 59) || + (binValue.tzSign < -1) || (binValue.tzSign > +1) || + ((binValue.tzSign == 0) && ((binValue.tzHour != 0) || (binValue.tzMinute != 0))) ) { + XMP_Throw ( "Invalid time zone values", kXMPErr_BadParam ); + } + + if ( binValue.tzSign == 0 ) { + *strValue += 'Z'; + } else { + snprintf ( buffer, sizeof(buffer), "+%02d:%02d", binValue.tzHour, binValue.tzMinute ); // AUDIT: Using sizeof for snprintf length is safe. + if ( binValue.tzSign < 0 ) buffer[0] = '-'; + *strValue += buffer; + } + + } + +} // ConvertFromDate + +// ------------------------------------------------------------------------------------------------- +// ConvertToBool +// ------------- +// +// Formally the string value should be "True" or "False", but we should be more flexible here. Map +// the string to lower case. Allow any of "true", "false", "t", "f", "1", or "0". + +/* class static */ bool +XMPUtils::ConvertToBool ( XMP_StringPtr strValue ) +{ + if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty convert-from string", kXMPErr_BadValue ); + + bool result = false; + XMP_VarString strObj ( strValue ); + + for ( XMP_VarStringPos ch = strObj.begin(); ch != strObj.end(); ++ch ) { + if ( ('A' <= *ch) && (*ch <= 'Z') ) *ch += 0x20; + } + + if ( (strObj == "true") || (strObj == "t") || (strObj == "1") ) { + result = true; + } else if ( (strObj == "false") || (strObj == "f") || (strObj == "0") ) { + result = false; + } else { + XMP_Throw ( "Invalid Boolean string", kXMPErr_BadParam ); + } + + return result; + +} // ConvertToBool + +// ------------------------------------------------------------------------------------------------- +// ConvertToInt +// ------------ + +/* class static */ XMP_Int32 +XMPUtils::ConvertToInt ( XMP_StringPtr strValue ) +{ + if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty convert-from string", kXMPErr_BadValue ); + + int count; + char nextCh; + XMP_Int32 result; + + if ( ! XMP_LitNMatch ( strValue, "0x", 2 ) ) { + count = sscanf ( strValue, "%d%c", &result, &nextCh ); + } else { + count = sscanf ( strValue, "%x%c", &result, &nextCh ); + } + + if ( count != 1 ) XMP_Throw ( "Invalid integer string", kXMPErr_BadParam ); + + return result; + +} // ConvertToInt + +// ------------------------------------------------------------------------------------------------- +// ConvertToInt64 +// -------------- + +/* class static */ XMP_Int64 +XMPUtils::ConvertToInt64 ( XMP_StringPtr strValue ) +{ + if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty convert-from string", kXMPErr_BadValue ); + + int count; + char nextCh; + XMP_Int64 result; + + if ( ! XMP_LitNMatch ( strValue, "0x", 2 ) ) { + count = sscanf ( strValue, "%lld%c", &result, &nextCh ); + } else { + count = sscanf ( strValue, "%llx%c", &result, &nextCh ); + } + + if ( count != 1 ) XMP_Throw ( "Invalid integer string", kXMPErr_BadParam ); + + return result; + +} // ConvertToInt64 + +// ------------------------------------------------------------------------------------------------- +// ConvertToFloat +// -------------- + +/* class static */ double +XMPUtils::ConvertToFloat ( XMP_StringPtr strValue ) +{ + if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty convert-from string", kXMPErr_BadValue ); + + XMP_VarString oldLocale; // Try to make sure number conversion uses '.' as the decimal point. + XMP_StringPtr oldLocalePtr = setlocale ( LC_ALL, 0 ); + if ( oldLocalePtr != 0 ) { + oldLocale.assign ( oldLocalePtr ); // Save the locale to be reset when exiting. + setlocale ( LC_ALL, "C" ); + } + + errno = 0; + char * numEnd; + double result = strtod ( strValue, &numEnd ); + int errnoSave = errno; // The setlocale call below might change errno. + + if ( ! oldLocale.empty() ) setlocale ( LC_ALL, oldLocale.c_str() ); // ! Reset locale before possible throw! + if ( (errnoSave != 0) || (*numEnd != 0) ) XMP_Throw ( "Invalid float string", kXMPErr_BadParam ); + + return result; + +} // ConvertToFloat + +// ------------------------------------------------------------------------------------------------- +// ConvertToDate +// ------------- +// +// Parse a date-time string according to ISO 8601 and http://www.w3.org/TR/NOTE-datetime: +// YYYY +// YYYY-MM +// YYYY-MM-DD +// YYYY-MM-DDThh:mmTZD +// YYYY-MM-DDThh:mm:ssTZD +// YYYY-MM-DDThh:mm:ss.sTZD +// +// YYYY = four-digit year +// MM = two-digit month (01=January, etc.) +// DD = two-digit day of month (01 through 31) +// hh = two digits of hour (00 through 23) +// mm = two digits of minute (00 through 59) +// ss = two digits of second (00 through 59) +// s = one or more digits representing a decimal fraction of a second +// TZD = time zone designator (Z or +hh:mm or -hh:mm) +// +// Note that ISO 8601 does not seem to allow years less than 1000 or greater than 9999. We allow +// any year, even negative ones. The year is formatted as "%.4d". The TZD is also optional in XMP, +// even though required in the W3C profile. Finally, Photoshop 8 (CS) sometimes created time-only +// values so we tolerate that. + +// *** Put the ISO format comments in the header documentation. + +/* class static */ void +XMPUtils::ConvertToDate ( XMP_StringPtr strValue, + XMP_DateTime * binValue ) +{ + if ( (strValue == 0) || (*strValue == 0) ) XMP_Throw ( "Empty convert-from string", kXMPErr_BadValue); + + size_t pos = 0; + XMP_Int32 temp; + + XMP_Assert ( sizeof(*binValue) == sizeof(XMP_DateTime) ); + (void) memset ( binValue, 0, sizeof(*binValue) ); // AUDIT: Safe, using sizeof destination. + + size_t strSize = strlen ( strValue ); + bool timeOnly = ( (strValue[0] == 'T') || + ((strSize >= 2) && (strValue[1] == ':')) || + ((strSize >= 3) && (strValue[2] == ':')) ); + + if ( ! timeOnly ) { + + binValue->hasDate = true; + + if ( strValue[0] == '-' ) pos = 1; + + temp = GatherInt ( strValue, &pos, "Invalid year in date string" ); // Extract the year. + if ( (strValue[pos] != 0) && (strValue[pos] != '-') ) XMP_Throw ( "Invalid date string, after year", kXMPErr_BadParam ); + if ( strValue[0] == '-' ) temp = -temp; + binValue->year = temp; + if ( strValue[pos] == 0 ) return; + + ++pos; + temp = GatherInt ( strValue, &pos, "Invalid month in date string" ); // Extract the month. + if ( (strValue[pos] != 0) && (strValue[pos] != '-') ) XMP_Throw ( "Invalid date string, after month", kXMPErr_BadParam ); + binValue->month = temp; + if ( strValue[pos] == 0 ) return; + + ++pos; + temp = GatherInt ( strValue, &pos, "Invalid day in date string" ); // Extract the day. + if ( (strValue[pos] != 0) && (strValue[pos] != 'T') ) XMP_Throw ( "Invalid date string, after day", kXMPErr_BadParam ); + binValue->day = temp; + if ( strValue[pos] == 0 ) return; + + // Allow year, month, and day to all be zero; implies the date portion is missing. + if ( (binValue->year != 0) || (binValue->month != 0) || (binValue->day != 0) ) { + // Temporary fix for bug 1269463, silently fix out of range month or day. + // if ( (binValue->month < 1) || (binValue->month > 12) ) XMP_Throw ( "Month is out of range", kXMPErr_BadParam ); + // if ( (binValue->day < 1) || (binValue->day > 31) ) XMP_Throw ( "Day is out of range", kXMPErr_BadParam ); + if ( binValue->month < 1 ) binValue->month = 1; + if ( binValue->month > 12 ) binValue->month = 12; + if ( binValue->day < 1 ) binValue->day = 1; + if ( binValue->day > 31 ) binValue->day = 31; + } + + } + + // If we get here there is more of the string, otherwise we would have returned above. + + if ( strValue[pos] == 'T' ) { + ++pos; + } else if ( ! timeOnly ) { + XMP_Throw ( "Invalid date string, missing 'T' after date", kXMPErr_BadParam ); + } + + binValue->hasTime = true; + + temp = GatherInt ( strValue, &pos, "Invalid hour in date string" ); // Extract the hour. + if ( strValue[pos] != ':' ) XMP_Throw ( "Invalid date string, after hour", kXMPErr_BadParam ); + if ( temp > 23 ) temp = 23; // *** 1269463: XMP_Throw ( "Hour is out of range", kXMPErr_BadParam ); + binValue->hour = temp; + // Don't check for done, we have to work up to the time zone. + + ++pos; + temp = GatherInt ( strValue, &pos, "Invalid minute in date string" ); // And the minute. + if ( (strValue[pos] != ':') && (strValue[pos] != 'Z') && + (strValue[pos] != '+') && (strValue[pos] != '-') && (strValue[pos] != 0) ) XMP_Throw ( "Invalid date string, after minute", kXMPErr_BadParam ); + if ( temp > 59 ) temp = 59; // *** 1269463: XMP_Throw ( "Minute is out of range", kXMPErr_BadParam ); + binValue->minute = temp; + // Don't check for done, we have to work up to the time zone. + + if ( strValue[pos] == ':' ) { + + ++pos; + temp = GatherInt ( strValue, &pos, "Invalid whole seconds in date string" ); // Extract the whole seconds. + if ( (strValue[pos] != '.') && (strValue[pos] != 'Z') && + (strValue[pos] != '+') && (strValue[pos] != '-') && (strValue[pos] != 0) ) { + XMP_Throw ( "Invalid date string, after whole seconds", kXMPErr_BadParam ); + } + if ( temp > 59 ) temp = 59; // *** 1269463: XMP_Throw ( "Whole second is out of range", kXMPErr_BadParam ); + binValue->second = temp; + // Don't check for done, we have to work up to the time zone. + + if ( strValue[pos] == '.' ) { + + ++pos; + size_t digits = pos; // Will be the number of digits later. + + temp = GatherInt ( strValue, &pos, "Invalid fractional seconds in date string" ); // Extract the fractional seconds. + if ( (strValue[pos] != 'Z') && (strValue[pos] != '+') && (strValue[pos] != '-') && (strValue[pos] != 0) ) { + XMP_Throw ( "Invalid date string, after fractional second", kXMPErr_BadParam ); + } + + digits = pos - digits; + for ( ; digits > 9; --digits ) temp = temp / 10; + for ( ; digits < 9; ++digits ) temp = temp * 10; + + if ( temp >= 1000*1000*1000 ) XMP_Throw ( "Fractional second is out of range", kXMPErr_BadParam ); + binValue->nanoSecond = temp; + // Don't check for done, we have to work up to the time zone. + + } + + } + + if ( strValue[pos] == 0 ) return; + + binValue->hasTimeZone = true; + + if ( strValue[pos] == 'Z' ) { + + ++pos; + + } else { + + if ( strValue[pos] == '+' ) { + binValue->tzSign = kXMP_TimeEastOfUTC; + } else if ( strValue[pos] == '-' ) { + binValue->tzSign = kXMP_TimeWestOfUTC; + } else { + XMP_Throw ( "Time zone must begin with 'Z', '+', or '-'", kXMPErr_BadParam ); + } + + ++pos; + temp = GatherInt ( strValue, &pos, "Invalid time zone hour in date string" ); // Extract the time zone hour. + if ( strValue[pos] != ':' ) XMP_Throw ( "Invalid date string, after time zone hour", kXMPErr_BadParam ); + if ( temp > 23 ) XMP_Throw ( "Time zone hour is out of range", kXMPErr_BadParam ); + binValue->tzHour = temp; + + ++pos; + temp = GatherInt ( strValue, &pos, "Invalid time zone minute in date string" ); // Extract the time zone minute. + if ( temp > 59 ) XMP_Throw ( "Time zone minute is out of range", kXMPErr_BadParam ); + binValue->tzMinute = temp; + + } + + if ( strValue[pos] != 0 ) XMP_Throw ( "Invalid date string, extra chars at end", kXMPErr_BadParam ); + +} // ConvertToDate + +// ------------------------------------------------------------------------------------------------- +// EncodeToBase64 +// -------------- +// +// Encode a string of raw data bytes in base 64 according to RFC 2045. For the encoding definition +// see section 6.8 in . Although it isn't needed for RDF, we +// do insert a linefeed character as a newline for every 76 characters of encoded output. + +/* class static */ void +XMPUtils::EncodeToBase64 ( XMP_StringPtr rawStr, + XMP_StringLen rawLen, + XMP_VarString * encodedStr ) +{ + if ( (rawStr == 0) && (rawLen != 0) ) XMP_Throw ( "Null raw data buffer", kXMPErr_BadParam ); + XMP_Assert ( encodedStr != 0 ); // Enforced by wrapper. + + encodedStr->erase(); + if ( rawLen == 0 ) return; + + char encChunk[4]; + + unsigned long in, out; + unsigned char c1, c2, c3; + unsigned long merge; + + const size_t outputSize = (rawLen / 3) * 4; // Approximate, might be small. + + encodedStr->reserve ( outputSize ); + + // ---------------------------------------------------------------------------------------- + // Each 6 bits of input produces 8 bits of output, so 3 input bytes become 4 output bytes. + // Process the whole chunks of 3 bytes first, then deal with any remainder. Be careful with + // the loop comparison, size-2 could be negative! + + for ( in = 0, out = 0; (in+2) < rawLen; in += 3, out += 4 ) { + + c1 = rawStr[in]; + c2 = rawStr[in+1]; + c3 = rawStr[in+2]; + + merge = (c1 << 16) + (c2 << 8) + c3; + + encChunk[0] = sBase64Chars [ merge >> 18 ]; + encChunk[1] = sBase64Chars [ (merge >> 12) & 0x3F ]; + encChunk[2] = sBase64Chars [ (merge >> 6) & 0x3F ]; + encChunk[3] = sBase64Chars [ merge & 0x3F ]; + + if ( out >= 76 ) { + encodedStr->append ( 1, kLF ); + out = 0; + } + encodedStr->append ( encChunk, 4 ); + + } + + // ------------------------------------------------------------------------------------------ + // The output must always be a multiple of 4 bytes. If there is a 1 or 2 byte input remainder + // we need to create another chunk. Zero pad with bits to a 6 bit multiple, then add one or + // two '=' characters to pad out to 4 bytes. + + switch ( rawLen - in ) { + + case 0: // Done, no remainder. + break; + + case 1: // One input byte remains. + + c1 = rawStr[in]; + merge = c1 << 16; + + encChunk[0] = sBase64Chars [ merge >> 18 ]; + encChunk[1] = sBase64Chars [ (merge >> 12) & 0x3F ]; + encChunk[2] = encChunk[3] = '='; + + if ( out >= 76 ) encodedStr->append ( 1, kLF ); + encodedStr->append ( encChunk, 4 ); + break; + + case 2: // Two input bytes remain. + + c1 = rawStr[in]; + c2 = rawStr[in+1]; + merge = (c1 << 16) + (c2 << 8); + + encChunk[0] = sBase64Chars [ merge >> 18 ]; + encChunk[1] = sBase64Chars [ (merge >> 12) & 0x3F ]; + encChunk[2] = sBase64Chars [ (merge >> 6) & 0x3F ]; + encChunk[3] = '='; + + if ( out >= 76 ) encodedStr->append ( 1, kLF ); + encodedStr->append ( encChunk, 4 ); + break; + + } + +} // EncodeToBase64 + +// ------------------------------------------------------------------------------------------------- +// DecodeFromBase64 +// ---------------- +// +// Decode a string of raw data bytes from base 64 according to RFC 2045. For the encoding definition +// see section 6.8 in . RFC 2045 talks about ignoring all "bad" +// input but warning about non-whitespace. For XMP use we ignore space, tab, LF, and CR. Any other +// bad input is rejected. + +/* class static */ void +XMPUtils::DecodeFromBase64 ( XMP_StringPtr encodedStr, + XMP_StringLen encodedLen, + XMP_VarString * rawStr ) +{ + if ( (encodedStr == 0) && (encodedLen != 0) ) XMP_Throw ( "Null encoded data buffer", kXMPErr_BadParam ); + XMP_Assert ( rawStr != 0 ); // Enforced by wrapper. + + rawStr->erase(); + if ( encodedLen == 0 ) return; + + unsigned char ch, rawChunk[3]; + unsigned long inStr, inChunk, inLimit, merge, padding; + + XMP_StringLen outputSize = (encodedLen / 4) * 3; // Only a close approximation. + + rawStr->reserve ( outputSize ); + + + // ---------------------------------------------------------------------------------------- + // Each 8 bits of input produces 6 bits of output, so 4 input bytes become 3 output bytes. + // Process all but the last 4 data bytes first, then deal with the final chunk. Whitespace + // in the input must be ignored. The first loop finds where the last 4 data bytes start and + // counts the number of padding equal signs. + + padding = 0; + for ( inStr = 0, inLimit = encodedLen; (inStr < 4) && (inLimit > 0); ) { + inLimit -= 1; // ! Don't do in the loop control, the decr/test order is wrong. + ch = encodedStr[inLimit]; + if ( ch == '=' ) { + padding += 1; // The equal sign padding is a data byte. + } else if ( DecodeBase64Char(ch) == 0xFF ) { + continue; // Ignore whitespace, don't increment inStr. + } else { + inStr += 1; + } + } + + // ! Be careful to count whitespace that is immediately before the final data. Otherwise + // ! middle portion will absorb the final data and mess up the final chunk processing. + + while ( (inLimit > 0) && (DecodeBase64Char(encodedStr[inLimit-1]) == 0xFF) ) --inLimit; + + if ( inStr == 0 ) return; // Nothing but whitespace. + if ( padding > 2 ) XMP_Throw ( "Invalid encoded string", kXMPErr_BadParam ); + + // ------------------------------------------------------------------------------------------- + // Now process all but the last chunk. The limit ensures that we have at least 4 data bytes + // left when entering the output loop, so the inner loop will succeed without overrunning the + // end of the data. At the end of the outer loop we might be past inLimit though. + + inStr = 0; + while ( inStr < inLimit ) { + + merge = 0; + for ( inChunk = 0; inChunk < 4; ++inStr ) { // ! Yes, increment inStr on each pass. + ch = DecodeBase64Char ( encodedStr [inStr] ); + if ( ch == 0xFF ) continue; // Ignore whitespace. + merge = (merge << 6) + ch; + inChunk += 1; + } + + rawChunk[0] = (unsigned char) (merge >> 16); + rawChunk[1] = (unsigned char) ((merge >> 8) & 0xFF); + rawChunk[2] = (unsigned char) (merge & 0xFF); + + rawStr->append ( (char*)rawChunk, 3 ); + + } + + // ------------------------------------------------------------------------------------------- + // Process the final, possibly partial, chunk of data. The input is always a multiple 4 bytes, + // but the raw data can be any length. The number of padding '=' characters determines if the + // final chunk has 1, 2, or 3 raw data bytes. + + XMP_Assert ( inStr < encodedLen ); + + merge = 0; + for ( inChunk = 0; inChunk < 4-padding; ++inStr ) { // ! Yes, increment inStr on each pass. + ch = DecodeBase64Char ( encodedStr[inStr] ); + if ( ch == 0xFF ) continue; // Ignore whitespace. + merge = (merge << 6) + ch; + inChunk += 1; + } + + if ( padding == 2 ) { + + rawChunk[0] = (unsigned char) (merge >> 4); + rawStr->append ( (char*)rawChunk, 1 ); + + } else if ( padding == 1 ) { + + rawChunk[0] = (unsigned char) (merge >> 10); + rawChunk[1] = (unsigned char) ((merge >> 2) & 0xFF); + rawStr->append ( (char*)rawChunk, 2 ); + + } else { + + rawChunk[0] = (unsigned char) (merge >> 16); + rawChunk[1] = (unsigned char) ((merge >> 8) & 0xFF); + rawChunk[2] = (unsigned char) (merge & 0xFF); + rawStr->append ( (char*)rawChunk, 3 ); + + } + +} // DecodeFromBase64 + +// ------------------------------------------------------------------------------------------------- +// PackageForJPEG +// -------------- + +/* class static */ void +XMPUtils::PackageForJPEG ( const XMPMeta & origXMP, + XMP_VarString * stdStr, + XMP_VarString * extStr, + XMP_VarString * digestStr ) +{ + XMP_Assert ( (stdStr != 0) && (extStr != 0) && (digestStr != 0) ); // ! Enforced by wrapper. + + enum { kStdXMPLimit = 65000 }; + static const char * kPacketTrailer = ""; + static size_t kTrailerLen = strlen ( kPacketTrailer ); + + XMP_VarString tempStr; + XMPMeta stdXMP, extXMP; + XMP_OptionBits keepItSmall = kXMP_UseCompactFormat | kXMP_OmitAllFormatting; + + stdStr->erase(); + extStr->erase(); + digestStr->erase(); + + // Try to serialize everything. Note that we're making internal calls to SerializeToBuffer, so + // we'll be getting back the pointer and length for its internal string. + + origXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 ); + #if Trace_PackageForJPEG + printf ( "\nXMPUtils::PackageForJPEG - Full serialize %d bytes\n", tempStr.size() ); + #endif + + if ( tempStr.size() > kStdXMPLimit ) { + + // Couldn't fit everything, make a copy of the input XMP and make sure there is no xmp:Thumbnails property. + + stdXMP.tree.options = origXMP.tree.options; + stdXMP.tree.name = origXMP.tree.name; + stdXMP.tree.value = origXMP.tree.value; + CloneOffspring ( &origXMP.tree, &stdXMP.tree ); + + if ( stdXMP.DoesPropertyExist ( kXMP_NS_XMP, "Thumbnails" ) ) { + stdXMP.DeleteProperty ( kXMP_NS_XMP, "Thumbnails" ); + stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 ); + #if Trace_PackageForJPEG + printf ( " Delete xmp:Thumbnails, %d bytes left\n", tempStr.size() ); + #endif + } + + } + + if ( tempStr.size() > kStdXMPLimit ) { + + // Still doesn't fit, move all of the Camera Raw namespace. Add a dummy value for xmpNote:HasExtendedXMP. + + stdXMP.SetProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP", "123456789-123456789-123456789-12", 0 ); + + XMP_NodePtrPos crSchemaPos; + XMP_Node * crSchema = FindSchemaNode ( &stdXMP.tree, kXMP_NS_CameraRaw, kXMP_ExistingOnly, &crSchemaPos ); + + if ( crSchema != 0 ) { + crSchema->parent = &extXMP.tree; + extXMP.tree.children.push_back ( crSchema ); + stdXMP.tree.children.erase ( crSchemaPos ); + stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 ); + #if Trace_PackageForJPEG + printf ( " Move Camera Raw schema, %d bytes left\n", tempStr.size() ); + #endif + } + + } + + if ( tempStr.size() > kStdXMPLimit ) { + + // Still doesn't fit, move photoshop:History. + + bool moved = MoveOneProperty ( stdXMP, &extXMP, kXMP_NS_Photoshop, "photoshop:History" ); + + if ( moved ) { + stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 ); + #if Trace_PackageForJPEG + printf ( " Move photoshop:History, %d bytes left\n", tempStr.size() ); + #endif + } + + } + + if ( tempStr.size() > kStdXMPLimit ) { + + // Still doesn't fit, move top level properties in order of estimated size. This is done by + // creating a multi-map that maps the serialized size to the string pair for the schema URI + // and top level property name. Since maps are inherently ordered, a reverse iteration of + // the map can be done to move the largest things first. We use a double loop to keep going + // until the serialization actually fits, in case the estimates are off. + + PropSizeMap propSizes; + CreateEstimatedSizeMap ( stdXMP, &propSizes ); + + #if Trace_PackageForJPEG + if ( ! propSizes.empty() ) { + printf ( " Top level property map, smallest to largest:\n" ); + PropSizeMap::iterator mapPos = propSizes.begin(); + PropSizeMap::iterator mapEnd = propSizes.end(); + for ( ; mapPos != mapEnd; ++mapPos ) { + size_t propSize = mapPos->first; + const char * schemaName = mapPos->second.first->c_str(); + const char * propName = mapPos->second.second->c_str(); + printf ( " %d bytes, %s in %s\n", propSize, propName, schemaName ); + } + } + #endif + + #if 0 // Trace_PackageForJPEG *** Xcode 2.3 on 10.4.7 has bugs in backwards iteration + if ( ! propSizes.empty() ) { + printf ( " Top level property map, largest to smallest:\n" ); + PropSizeMap::iterator mapPos = propSizes.end(); + PropSizeMap::iterator mapBegin = propSizes.begin(); + for ( --mapPos; true; --mapPos ) { + size_t propSize = mapPos->first; + const char * schemaName = mapPos->second.first->c_str(); + const char * propName = mapPos->second.second->c_str(); + printf ( " %d bytes, %s in %s\n", propSize, propName, schemaName ); + if ( mapPos == mapBegin ) break; + } + } + #endif + + // Outer loop to make sure enough is actually moved. + + while ( (tempStr.size() > kStdXMPLimit) && (! propSizes.empty()) ) { + + // Inner loop, move what seems to be enough according to the estimates. + + size_t tempLen = tempStr.size(); + while ( (tempLen > kStdXMPLimit) && (! propSizes.empty()) ) { + + size_t propSize = MoveLargestProperty ( stdXMP, &extXMP, propSizes ); + XMP_Assert ( propSize > 0 ); + + if ( propSize > tempLen ) propSize = tempLen; // ! Don't go negative. + tempLen -= propSize; + + } + + // Reserialize the remaining standard XMP. + + stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 ); + + } + + } + + if ( tempStr.size() > kStdXMPLimit ) { + // Still doesn't fit, throw an exception and let the client decide what to do. + // ! This should never happen with the policy of moving any and all top level properties. + XMP_Throw ( "Can't reduce XMP enough for JPEG file", kXMPErr_TooLargeForJPEG ); + } + + // Set the static output strings. + + if ( extXMP.tree.children.empty() ) { + + // Just have the standard XMP. + *stdStr = tempStr; + + } else { + + // Have extended XMP. Serialize it, compute the digest, reset xmpNote:HasExtendedXMP, and + // reserialize the standard XMP. + + extXMP.SerializeToBuffer ( &tempStr, (keepItSmall | kXMP_OmitPacketWrapper), 0, "", "", 0 ); + *extStr = tempStr; + + XMP_Uns8 digest [16]; + + { + context_md5_t ctx; + + MD5Init(&ctx); + MD5Update(&ctx, (unsigned char*)tempStr.c_str(), (unsigned int)tempStr.size() ); + MD5Final(digest, &ctx); + } + + digestStr->reserve ( 32 ); + for ( size_t i = 0; i < 16; ++i ) { + XMP_Uns8 byte = digest[i]; + digestStr->push_back ( kHexDigits [ byte>>4 ] ); + digestStr->push_back ( kHexDigits [ byte&0xF ] ); + } + + stdXMP.SetProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP", digestStr->c_str(), 0 ); + stdXMP.SerializeToBuffer ( &tempStr, keepItSmall, 1, "", "", 0 ); + *stdStr = tempStr; + + } + + // Adjust the standard XMP padding to be up to 2KB. + + XMP_Assert ( (stdStr->size() > kTrailerLen) && (stdStr->size() <= kStdXMPLimit) ); + const char * packetEnd = stdStr->c_str() + stdStr->size() - kTrailerLen; + XMP_Assert ( XMP_LitMatch ( packetEnd, kPacketTrailer ) ); + + size_t extraPadding = kStdXMPLimit - stdStr->size(); // ! Do this before erasing the trailer. + if ( extraPadding > 2047 ) extraPadding = 2047; + stdStr->erase ( stdStr->size() - kTrailerLen ); + stdStr->append ( extraPadding, ' ' ); + stdStr->append ( kPacketTrailer ); + +} // PackageForJPEG + +// ------------------------------------------------------------------------------------------------- +// MergeFromJPEG +// ------------- +// +// Copy all of the top level properties from extendedXMP to fullXMP, replacing any duplicates. +// Delete the xmpNote:HasExtendedXMP property from fullXMP. + +/* class static */ void +XMPUtils::MergeFromJPEG ( XMPMeta * fullXMP, + const XMPMeta & extendedXMP ) +{ + + XMP_OptionBits apFlags = (kXMPTemplate_ReplaceExistingProperties | kXMPTemplate_IncludeInternalProperties); + XMPUtils::ApplyTemplate ( fullXMP, extendedXMP, apFlags ); + fullXMP->DeleteProperty ( kXMP_NS_XMP_Note, "HasExtendedXMP" ); + +} // MergeFromJPEG + +// ------------------------------------------------------------------------------------------------- +// CurrentDateTime +// --------------- + +/* class static */ void +XMPUtils::CurrentDateTime ( XMP_DateTime * xmpTime ) +{ + XMP_Assert ( xmpTime != 0 ); // ! Enforced by wrapper. + + ansi_tt binTime = ansi_time(0); + if ( binTime == -1 ) XMP_Throw ( "Failure from ANSI C time function", kXMPErr_ExternalFailure ); + ansi_tm currTime; + ansi_localtime ( &binTime, &currTime ); + + xmpTime->year = currTime.tm_year + 1900; + xmpTime->month = currTime.tm_mon + 1; + xmpTime->day = currTime.tm_mday; + xmpTime->hasDate = true; + + xmpTime->hour = currTime.tm_hour; + xmpTime->minute = currTime.tm_min; + xmpTime->second = currTime.tm_sec; + xmpTime->nanoSecond = 0; + xmpTime->hasTime = true; + + xmpTime->tzSign = 0; + xmpTime->tzHour = 0; + xmpTime->tzMinute = 0; + xmpTime->hasTimeZone = false; // ! Needed for SetTimeZone. + XMPUtils::SetTimeZone ( xmpTime ); + +} // CurrentDateTime + +// ------------------------------------------------------------------------------------------------- +// SetTimeZone +// ----------- +// +// Sets just the time zone part of the time. Useful for determining the local time zone or for +// converting a "zone-less" time to a proper local time. The ANSI C time functions are smart enough +// to do all the right stuff, as long as we call them properly! + +/* class static */ void +XMPUtils::SetTimeZone ( XMP_DateTime * xmpTime ) +{ + XMP_Assert ( xmpTime != 0 ); // ! Enforced by wrapper. + + VerifyDateTimeFlags ( xmpTime ); + + if ( xmpTime->hasTimeZone ) { + XMP_Throw ( "SetTimeZone can only be used on zone-less times", kXMPErr_BadParam ); + } + + // Create ansi_tt form of the input time. Need the ansi_tm form to make the ansi_tt form. + + ansi_tt ttTime; + ansi_tm tmLocal, tmUTC; + + if ( (xmpTime->year == 0) && (xmpTime->month == 0) && (xmpTime->day == 0) ) { + ansi_tt now = ansi_time(0); + if ( now == -1 ) XMP_Throw ( "Failure from ANSI C time function", kXMPErr_ExternalFailure ); + ansi_localtime ( &now, &tmLocal ); + } else { + tmLocal.tm_year = xmpTime->year - 1900; + while ( tmLocal.tm_year < 70 ) tmLocal.tm_year += 4; // ! Some versions of mktime barf on years before 1970. + tmLocal.tm_mon = xmpTime->month - 1; + tmLocal.tm_mday = xmpTime->day; + } + + tmLocal.tm_hour = xmpTime->hour; + tmLocal.tm_min = xmpTime->minute; + tmLocal.tm_sec = xmpTime->second; + tmLocal.tm_isdst = -1; // Don't know if daylight time is in effect. + + ttTime = ansi_mktime ( &tmLocal ); + if ( ttTime == -1 ) XMP_Throw ( "Failure from ANSI C mktime function", kXMPErr_ExternalFailure ); + + // Convert back to a localized ansi_tm time and get the corresponding UTC ansi_tm time. + + ansi_localtime ( &ttTime, &tmLocal ); + ansi_gmtime ( &ttTime, &tmUTC ); + + // Get the offset direction and amount. + + ansi_tm tmx = tmLocal; // ! Note that mktime updates the ansi_tm parameter, messing up difftime! + ansi_tm tmy = tmUTC; + tmx.tm_isdst = tmy.tm_isdst = 0; + ansi_tt ttx = ansi_mktime ( &tmx ); + ansi_tt tty = ansi_mktime ( &tmy ); + double diffSecs; + + if ( (ttx != -1) && (tty != -1) ) { + diffSecs = ansi_difftime ( ttx, tty ); + } else { + #if XMP_MacBuild | XMP_iOSBuild + // Looks like Apple's mktime is buggy - see W1140533. But the offset is visible. + diffSecs = tmLocal.tm_gmtoff; + #else + // Win and UNIX don't have a visible offset. Make sure we know about the failure, + // then try using the current date/time as a close fallback. + ttTime = ansi_time(0); + if ( ttTime == -1 ) XMP_Throw ( "Failure from ANSI C time function", kXMPErr_ExternalFailure ); + ansi_localtime ( &ttTime, &tmx ); + ansi_gmtime ( &ttTime, &tmy ); + tmx.tm_isdst = tmy.tm_isdst = 0; + ttx = ansi_mktime ( &tmx ); + tty = ansi_mktime ( &tmy ); + if ( (ttx == -1) || (tty == -1) ) XMP_Throw ( "Failure from ANSI C mktime function", kXMPErr_ExternalFailure ); + diffSecs = ansi_difftime ( ttx, tty ); + #endif + } + + if ( diffSecs > 0.0 ) { + xmpTime->tzSign = kXMP_TimeEastOfUTC; + } else if ( diffSecs == 0.0 ) { + xmpTime->tzSign = kXMP_TimeIsUTC; + } else { + xmpTime->tzSign = kXMP_TimeWestOfUTC; + diffSecs = -diffSecs; + } + xmpTime->tzHour = XMP_Int32 ( diffSecs / 3600.0 ); + xmpTime->tzMinute = XMP_Int32 ( (diffSecs / 60.0) - (xmpTime->tzHour * 60.0) ); + + xmpTime->hasTimeZone = xmpTime->hasTime = true; + + // *** Save the tm_isdst flag in a qualifier? + + XMP_Assert ( (0 <= xmpTime->tzHour) && (xmpTime->tzHour <= 23) ); + XMP_Assert ( (0 <= xmpTime->tzMinute) && (xmpTime->tzMinute <= 59) ); + XMP_Assert ( (-1 <= xmpTime->tzSign) && (xmpTime->tzSign <= +1) ); + XMP_Assert ( (xmpTime->tzSign == 0) ? ((xmpTime->tzHour == 0) && (xmpTime->tzMinute == 0)) : + ((xmpTime->tzHour != 0) || (xmpTime->tzMinute != 0)) ); + +} // SetTimeZone + +// ------------------------------------------------------------------------------------------------- +// ConvertToUTCTime +// ---------------- + +/* class static */ void +XMPUtils::ConvertToUTCTime ( XMP_DateTime * time ) +{ + XMP_Assert ( time != 0 ); // ! Enforced by wrapper. + + VerifyDateTimeFlags ( time ); + + if ( ! time->hasTimeZone ) return; // Do nothing if there is no current time zone. + + XMP_Assert ( (0 <= time->tzHour) && (time->tzHour <= 23) ); + XMP_Assert ( (0 <= time->tzMinute) && (time->tzMinute <= 59) ); + XMP_Assert ( (-1 <= time->tzSign) && (time->tzSign <= +1) ); + XMP_Assert ( (time->tzSign == 0) ? ((time->tzHour == 0) && (time->tzMinute == 0)) : + ((time->tzHour != 0) || (time->tzMinute != 0)) ); + + if ( time->tzSign == kXMP_TimeEastOfUTC ) { + // We are before (east of) GMT, subtract the offset from the time. + time->hour -= time->tzHour; + time->minute -= time->tzMinute; + } else if ( time->tzSign == kXMP_TimeWestOfUTC ) { + // We are behind (west of) GMT, add the offset to the time. + time->hour += time->tzHour; + time->minute += time->tzMinute; + } + + AdjustTimeOverflow ( time ); + time->tzSign = time->tzHour = time->tzMinute = 0; + +} // ConvertToUTCTime + +// ------------------------------------------------------------------------------------------------- +// ConvertToLocalTime +// ------------------ + +/* class static */ void +XMPUtils::ConvertToLocalTime ( XMP_DateTime * time ) +{ + XMP_Assert ( time != 0 ); // ! Enforced by wrapper. + + VerifyDateTimeFlags ( time ); + + if ( ! time->hasTimeZone ) return; // Do nothing if there is no current time zone. + + XMP_Assert ( (0 <= time->tzHour) && (time->tzHour <= 23) ); + XMP_Assert ( (0 <= time->tzMinute) && (time->tzMinute <= 59) ); + XMP_Assert ( (-1 <= time->tzSign) && (time->tzSign <= +1) ); + XMP_Assert ( (time->tzSign == 0) ? ((time->tzHour == 0) && (time->tzMinute == 0)) : + ((time->tzHour != 0) || (time->tzMinute != 0)) ); + + ConvertToUTCTime ( time ); // The existing time zone might not be the local one. + time->hasTimeZone = false; // ! Needed for SetTimeZone. + SetTimeZone ( time ); // Fill in the local timezone offset, then adjust the time. + + if ( time->tzSign > 0 ) { + // We are before (east of) GMT, add the offset to the time. + time->hour += time->tzHour; + time->minute += time->tzMinute; + } else if ( time->tzSign < 0 ) { + // We are behind (west of) GMT, subtract the offset from the time. + time->hour -= time->tzHour; + time->minute -= time->tzMinute; + } + + AdjustTimeOverflow ( time ); + +} // ConvertToLocalTime + +// ------------------------------------------------------------------------------------------------- +// CompareDateTime +// --------------- + +/* class static */ int +XMPUtils::CompareDateTime ( const XMP_DateTime & _in_left, + const XMP_DateTime & _in_right ) +{ + int result = 0; + + XMP_DateTime left = _in_left; + XMP_DateTime right = _in_right; + + VerifyDateTimeFlags ( &left ); + VerifyDateTimeFlags ( &right ); + + // Can't compare if one has a date and the other does not. + if ( left.hasDate != right.hasDate ) return 0; // Throw? + + if ( left.hasTimeZone & right.hasTimeZone ) { + // If both times have zones then convert them to UTC, otherwise assume the same zone. + ConvertToUTCTime ( &left ); + ConvertToUTCTime ( &right ); + } + + if ( left.hasDate ) { + + XMP_Assert ( right.hasDate ); + + if ( left.year < right.year ) { + result = -1; + } else if ( left.year > right.year ) { + result = +1; + } else if ( left.month < right.month ) { + result = -1; + } else if ( left.month > right.month ) { + result = +1; + } else if ( left.day < right.day ) { + result = -1; + } else if ( left.day > right.day ) { + result = +1; + } + + if ( result != 0 ) return result; + + } + + if ( left.hasTime & right.hasTime ) { + + // Ignore the time parts if either value is date-only. + + if ( left.hour < right.hour ) { + result = -1; + } else if ( left.hour > right.hour ) { + result = +1; + } else if ( left.minute < right.minute ) { + result = -1; + } else if ( left.minute > right.minute ) { + result = +1; + } else if ( left.second < right.second ) { + result = -1; + } else if ( left.second > right.second ) { + result = +1; + } else if ( left.nanoSecond < right.nanoSecond ) { + result = -1; + } else if ( left.nanoSecond > right.nanoSecond ) { + result = +1; + } else { + result = 0; + } + + } + + return result; + +} // CompareDateTime + +// ================================================================================================= + +std::string& XMPUtils::Trim( std::string& string ) +{ + size_t pos = string.find_last_not_of( *WhiteSpaceStrPtr ); + + if ( pos != std::string::npos ) { + string.erase( pos + 1 ); + pos = string.find_first_not_of( *WhiteSpaceStrPtr ); + if(pos != std::string::npos) string.erase(0, pos); + } else { + string.erase( string.begin(), string.end() ); + } + return string; +} + +std::string * XMPUtils::WhiteSpaceStrPtr = NULL; + +// ================================================================================================= diff --git a/source/lib/xmp_core/XMPUtils.hpp b/source/lib/xmp_core/XMPUtils.hpp new file mode 100644 index 0000000..1c99041 --- /dev/null +++ b/source/lib/xmp_core/XMPUtils.hpp @@ -0,0 +1,198 @@ +#ifndef __XMPUtils_hpp__ +#define __XMPUtils_hpp__ + +// ================================================================================================= +// Copyright 2003 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" +#include "public/include/XMP_Const.h" + +#include "XMPMeta.hpp" +#include "XMPCore_Impl.hpp" +#include "public/include/client-glue/WXMPUtils.hpp" + +// ------------------------------------------------------------------------------------------------- + +class XMPUtils { +public: + + static bool + Initialize(); // ! For internal use only! + + static void + Terminate() RELEASE_NO_THROW; // ! For internal use only! + + // --------------------------------------------------------------------------------------------- + + static void + ComposeArrayItemPath ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_VarString * fullPath ); + + static void + ComposeStructFieldPath ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_VarString * fullPath ); + + static void + ComposeQualifierPath ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_VarString * fullPath ); + + static void + ComposeLangSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr langName, + XMP_VarString * fullPath ); + + static void + ComposeFieldSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + XMP_VarString * fullPath ); + + // --------------------------------------------------------------------------------------------- + + static void + ConvertFromBool ( bool binValue, + XMP_VarString * strValue ); + + static void + ConvertFromInt ( XMP_Int32 binValue, + XMP_StringPtr format, + XMP_VarString * strValue ); + + static void + ConvertFromInt64 ( XMP_Int64 binValue, + XMP_StringPtr format, + XMP_VarString * strValue ); + + static void + ConvertFromFloat ( double binValue, + XMP_StringPtr format, + XMP_VarString * strValue ); + + static void + ConvertFromDate ( const XMP_DateTime & binValue, + XMP_VarString * strValue ); + + // --------------------------------------------------------------------------------------------- + + static bool + ConvertToBool ( XMP_StringPtr strValue ); + + static XMP_Int32 + ConvertToInt ( XMP_StringPtr strValue ); + + static XMP_Int64 + ConvertToInt64 ( XMP_StringPtr strValue ); + + static double + ConvertToFloat ( XMP_StringPtr strValue ); + + static void + ConvertToDate ( XMP_StringPtr strValue, + XMP_DateTime * binValue ); + + // --------------------------------------------------------------------------------------------- + + static void + CurrentDateTime ( XMP_DateTime * time ); + + static void + SetTimeZone ( XMP_DateTime * time ); + + static void + ConvertToUTCTime ( XMP_DateTime * time ); + + static void + ConvertToLocalTime ( XMP_DateTime * time ); + + static int + CompareDateTime ( const XMP_DateTime & left, + const XMP_DateTime & right ); + // --------------------------------------------------------------------------------------------- + + static void + EncodeToBase64 ( XMP_StringPtr rawStr, + XMP_StringLen rawLen, + XMP_VarString * encodedStr ); + + static void + DecodeFromBase64 ( XMP_StringPtr encodedStr, + XMP_StringLen encodedLen, + XMP_VarString * rawStr ); + + // --------------------------------------------------------------------------------------------- + + static void + PackageForJPEG ( const XMPMeta & xmpObj, + XMP_VarString * stdStr, + XMP_VarString * extStr, + XMP_VarString * digestStr ); + + static void + MergeFromJPEG ( XMPMeta * fullXMP, + const XMPMeta & extendedXMP ); + + // --------------------------------------------------------------------------------------------- + + static void + CatenateArrayItems ( const XMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr separator, + XMP_StringPtr quotes, + XMP_OptionBits options, + XMP_VarString * catedStr ); + + static void + SeparateArrayItems ( XMPMeta * xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + XMP_StringPtr catedStr ); + + static void + ApplyTemplate ( XMPMeta * workingXMP, + const XMPMeta & templateXMP, + XMP_OptionBits actions ); + + static void + RemoveProperties ( XMPMeta * xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options ); + + static void + DuplicateSubtree ( const XMPMeta & source, + XMPMeta * dest, + XMP_StringPtr sourceNS, + XMP_StringPtr sourceRoot, + XMP_StringPtr destNS, + XMP_StringPtr destRoot, + XMP_OptionBits options ); + + // --------------------------------------------------------------------------------------------- + + static std::string& Trim(std::string& string); + + static std::string * WhiteSpaceStrPtr; + +}; // XMPUtils + +// ================================================================================================= + +#endif // __XMPUtils_hpp__ diff --git a/source/lib/xmp_core/XMP_BuildInfo.h b/source/lib/xmp_core/XMP_BuildInfo.h new file mode 100644 index 0000000..35fe00e --- /dev/null +++ b/source/lib/xmp_core/XMP_BuildInfo.h @@ -0,0 +1,17 @@ +#ifndef __XMP_BuildInfo_h__ +#define __XMP_BuildInfo_h__ 1 + +/* +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= +*/ + +#define kXMP_Copyright Copyright (c) 2013 +#define kXMP_CopyrightStr "Copyright (c) 2013" + +#endif /* __XMP_BuildInfo_h__ */ diff --git a/source/lib/xmp_core/XMP_LibUtils.cpp b/source/lib/xmp_core/XMP_LibUtils.cpp new file mode 100644 index 0000000..507294e --- /dev/null +++ b/source/lib/xmp_core/XMP_LibUtils.cpp @@ -0,0 +1,705 @@ +// ================================================================================================= +// Copyright 2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" + +#include "XMP_LibUtils.hpp" + +#include "UnicodeInlines.incl_cpp" + +#include +#include + +// ================================================================================================= + +#ifndef TraceThreadLocks + #define TraceThreadLocks 0 +#endif + +// ------------------------------------------------------------------------------------------------- + +extern "C" bool Initialize_LibUtils() +{ + return true; +} + +// ------------------------------------------------------------------------------------------------- + +extern "C" void Terminate_LibUtils(){ + // Nothing to do. +} + +// ================================================================================================= +// Error notifications +// ================================================================================================= + +bool GenericErrorCallback::CheckLimitAndSeverity ( XMP_ErrorSeverity severity ) const +{ + + if ( this->limit == 0 ) return true; // Always notify if the limit is zero. + if ( severity < this->topSeverity ) return false; // Don't notify, don't count. + + if ( severity > this->topSeverity ) { + this->topSeverity = severity; + this->notifications = 0; + } + + this->notifications += 1; + return (this->notifications <= this->limit); + +} // GenericErrorCallback::CheckLimitAndSeverity + +// ================================================================================================= + +void GenericErrorCallback::NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error, XMP_StringPtr filePath /*= 0 */ ) const +{ + bool notifyClient = CanNotify() && !error.IsNotified(); + bool returnAndRecover (severity == kXMPErrSev_Recoverable); + + if ( notifyClient ) { + error.SetNotified(); + notifyClient = CheckLimitAndSeverity ( severity ); + if ( notifyClient ) { + returnAndRecover &= ClientCallbackWrapper( filePath, severity, error.GetID(), error.GetErrMsg() ); + } + } + + if ( ! returnAndRecover ) XMP_Error_Throw ( error ); + +} + +// ================================================================================================= +// Thread synchronization locks +// ================================================================================================= + +XMP_ReadWriteLock::XMP_ReadWriteLock() : beingWritten(false) +{ + #if XMP_DebugBuild && HaveAtomicIncrDecr + this->lockCount = 0; + // Atomic counter must be 32 or 64 bits and naturally aligned. + size_t counterSize = sizeof ( XMP_AtomicCounter ); + size_t counterOffset = XMP_OffsetOf ( XMP_ReadWriteLock, lockCount ); + XMP_Assert ( (counterSize == 4) || (counterSize == 8) ); // Counter must be 32 or 64 bits. + XMP_Assert ( (counterOffset & (counterSize-1)) == 0 ); // Counter must be naturally aligned. + #endif + XMP_BasicRWLock_Initialize ( this->lock ); + #if TraceThreadLocks + fprintf ( stderr, "Created lock %.8X\n", this ); + #endif +} + +// --------------------------------------------------------------------------------------------- + +XMP_ReadWriteLock::~XMP_ReadWriteLock() +{ + #if TraceThreadLocks + fprintf ( stderr, "Deleting lock %.8X\n", this ); + #endif + #if XMP_DebugBuild && HaveAtomicIncrDecr + XMP_Assert ( this->lockCount == 0 ); + #endif + XMP_BasicRWLock_Terminate ( this->lock ); +} + +// --------------------------------------------------------------------------------------------- + +void XMP_ReadWriteLock::Acquire ( bool forWriting ) +{ + #if TraceThreadLocks + fprintf ( stderr, "Acquiring lock %.8X for %s, count %d%s\n", + this, (forWriting ? "writing" : "reading"), this->lockCount, (this->beingWritten ? ", being written" : "") ); + #endif + + if ( forWriting ) { + XMP_BasicRWLock_AcquireForWrite ( this->lock ); + #if XMP_DebugBuild && HaveAtomicIncrDecr + XMP_Assert ( this->lockCount == 0 ); + #endif + } else { + XMP_BasicRWLock_AcquireForRead ( this->lock ); + XMP_Assert ( ! this->beingWritten ); + } + #if XMP_DebugBuild && HaveAtomicIncrDecr + XMP_AtomicIncrement ( this->lockCount ); + #endif + this->beingWritten = forWriting; + + #if TraceThreadLocks + fprintf ( stderr, "Acquired lock %.8X for %s, count %d%s\n", + this, (forWriting ? "writing" : "reading"), this->lockCount, (this->beingWritten ? ", being written" : "") ); + #endif +} + +// --------------------------------------------------------------------------------------------- + +void XMP_ReadWriteLock::Release() +{ + #if TraceThreadLocks + fprintf ( stderr, "Releasing lock %.8X, count %d%s\n", this, this->lockCount, (this->beingWritten ? ", being written" : "") ); + #endif + + #if XMP_DebugBuild && HaveAtomicIncrDecr + XMP_Assert ( this->lockCount > 0 ); + XMP_AtomicDecrement ( this->lockCount ); // ! Do these before unlocking, that might release a waiting thread. + #endif + bool forWriting = this->beingWritten; + this->beingWritten = false; + + if ( forWriting ) { + XMP_BasicRWLock_ReleaseFromWrite ( this->lock ); + } else { + XMP_BasicRWLock_ReleaseFromRead ( this->lock ); + } + + #if TraceThreadLocks + fprintf ( stderr, "Released lock %.8X, count %d%s\n", this, this->lockCount, (this->beingWritten ? ", being written" : "") ); + #endif +} + +// ================================================================================================= + +#if UseHomeGrownLock + + #if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild + + // ----------------------------------------------------------------------------------------- + + // About pthread mutexes and conditions: + // + // The mutex protecting the condition must be locked before waiting for the condition. A + // thread can wait for a condition to be signaled by calling the pthread_cond_wait + // subroutine. The subroutine atomically unlocks the mutex and blocks the calling thread + // until the condition is signaled. When the call returns, the mutex is locked again. + + #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); } + #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); } + + #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); } + #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); } + + #define InitializeBasicQueue(queue) { int err = pthread_cond_init ( &queue, 0 ); XMP_Enforce ( err == 0 ); } + #define TerminateBasicQueue(queue) { int err = pthread_cond_destroy ( &queue ); XMP_Enforce ( err == 0 ); } + + #define WaitOnBasicQueue(queue,mutex) { int err = pthread_cond_wait ( &queue, &mutex ); XMP_Enforce ( err == 0 ); } + #define ReleaseOneBasicQueue(queue) { int err = pthread_cond_signal ( &queue ); XMP_Enforce ( err == 0 ); } + #define ReleaseAllBasicQueue(queue) { int err = pthread_cond_broadcast ( &queue ); XMP_Enforce ( err == 0 ); } + + // ----------------------------------------------------------------------------------------- + + #elif XMP_WinBuild + + // ----------------------------------------------------------------------------------------- + + #define InitializeBasicMutex(mutex) { InitializeCriticalSection ( &mutex ); } + #define TerminateBasicMutex(mutex) { DeleteCriticalSection ( &mutex ); } + + #define AcquireBasicMutex(mutex) { EnterCriticalSection ( &mutex ); } + #define ReleaseBasicMutex(mutex) { LeaveCriticalSection ( &mutex ); } + + #if ! BuildLocksForWinXP + + // About Win32 condition variables (not on XP): + // + // Condition variables enable threads to atomically release a lock and enter the + // sleeping state. They can be used with critical sections or slim reader/writer (SRW) + // locks. Condition variables support operations that "wake one" or "wake all" waiting + // threads. After a thread is woken, it re-acquires the lock it released when the thread + // entered the sleeping state. + + #define InitializeBasicQueue(queue) { InitializeConditionVariable ( &queue ); } + #define TerminateBasicQueue(queue) /* Do nothing. */ + + #define WaitOnBasicQueue(queue,mutex) \ + { BOOL ok = SleepConditionVariableCS ( &queue, &mutex, INFINITE /* timeout */ ); XMP_Enforce ( ok ); } + + #define ReleaseOneBasicQueue(queue) { WakeConditionVariable ( &queue ); } + #define ReleaseAllBasicQueue(queue) { WakeAllConditionVariable ( &queue ); } + + #else + + // Need to create our own queue for Windows XP. This is not a general queue, it depends + // on the usage inside XMP_HomeGrownLock where the queueMutex guarantees that the + // queueing operations are done single threaded. + + #define InitializeBasicQueue(queue) /* Do nothing. */ + #define TerminateBasicQueue(queue) /* Do nothing. */ + + #define WaitOnBasicQueue(queue,mutex) { queue.Wait ( mutex ); } + #define ReleaseOneBasicQueue(queue) { queue.ReleaseOne(); } + #define ReleaseAllBasicQueue(queue) { queue.ReleaseAll(); } + + // ------------------------------------------------------------------------------------- + + XMP_WinXP_HGQueue::XMP_WinXP_HGQueue() : queueEvent(0), waitCount(0), releaseAll(false) + { + this->queueEvent = CreateEvent ( NULL, FALSE, TRUE, NULL ); // Auto reset, initially clear. + XMP_Enforce ( this->queueEvent != 0 ); + } + + // ------------------------------------------------------------------------------------- + + XMP_WinXP_HGQueue::~XMP_WinXP_HGQueue() + { + CloseHandle ( this->queueEvent ); + } + + // ------------------------------------------------------------------------------------- + + void XMP_WinXP_HGQueue::Wait ( XMP_BasicMutex & queueMutex ) + { + ++this->waitCount; // ! Does not need atomic increment, protected by queue mutex. + ReleaseBasicMutex ( queueMutex ); + DWORD status = WaitForSingleObject ( this->queueEvent, INFINITE ); + if ( status != WAIT_OBJECT_0 ) XMP_Throw ( "Failure from WaitForSingleObject", kXMPErr_ExternalFailure ); + AcquireBasicMutex ( queueMutex ); + --this->waitCount; // ! Does not need atomic decrement, protected by queue mutex. + + if ( this->releaseAll ) { + if ( this->waitCount == 0 ) { + this->releaseAll = false; + } else { + BOOL ok = SetEvent ( this->queueEvent ); + if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure ); + } + } + } + + // ------------------------------------------------------------------------------------- + + void XMP_WinXP_HGQueue::ReleaseOne() + { + XMP_Assert ( ! this->releaseAll ); + BOOL ok = SetEvent ( this->queueEvent ); + if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure ); + } + + // ------------------------------------------------------------------------------------- + + void XMP_WinXP_HGQueue::ReleaseAll() + { + this->releaseAll = true; + BOOL ok = SetEvent ( this->queueEvent ); + if ( ! ok ) XMP_Throw ( "Failure from SetEvent", kXMPErr_ExternalFailure ); + } + + #endif + + // ----------------------------------------------------------------------------------------- + + #endif + + // ============================================================================================= + + XMP_HomeGrownLock::XMP_HomeGrownLock() : lockCount(0), readersWaiting(0), writersWaiting(0), beingWritten(false) + { + InitializeBasicMutex ( this->queueMutex ); + InitializeBasicQueue ( this->writerQueue ); + InitializeBasicQueue ( this->readerQueue ); + } + + // ============================================================================================= + + XMP_HomeGrownLock::~XMP_HomeGrownLock() + { + TerminateBasicMutex ( this->queueMutex ); + TerminateBasicQueue ( this->writerQueue ); + TerminateBasicQueue ( this->readerQueue ); + } + + // ============================================================================================= + + void XMP_HomeGrownLock::AcquireForRead() + { + XMP_AutoMutex autoMutex ( &this->queueMutex ); + + ++this->readersWaiting; // ! Does not need atomic increment, protected by queue mutex. + while ( (this->beingWritten) || (this->writersWaiting > 0) ) { + // Don't allow more readers if writers are waiting. + WaitOnBasicQueue ( this->readerQueue, this->queueMutex ); + } + --this->readersWaiting; // ! Does not need atomic decrement, protected by queue mutex. + XMP_Assert ( ! this->beingWritten ); + + ++this->lockCount; // ! Does not need atomic increment, protected by queue mutex. + } + + // ============================================================================================= + + void XMP_HomeGrownLock::AcquireForWrite() + { + XMP_AutoMutex autoMutex ( &this->queueMutex ); + + ++this->writersWaiting; // ! Does not need atomic increment, protected by queue mutex. + while ( this->lockCount > 0 ) { + WaitOnBasicQueue ( this->writerQueue, this->queueMutex ); + } + --this->writersWaiting; // ! Does not need atomic decrement, protected by queue mutex. + XMP_Assert ( (! this->beingWritten) && (this->lockCount == 0) ); + + ++this->lockCount; // ! Does not need atomic increment, protected by queue mutex. + this->beingWritten = true; + } + + // ============================================================================================= + + void XMP_HomeGrownLock::ReleaseFromRead() + { + XMP_AutoMutex autoMutex ( &this->queueMutex ); + + XMP_Assert ( (! this->beingWritten) && (this->lockCount > 0) ); + --this->lockCount; // ! Does not need atomic decrement, protected by queue mutex. + + if ( this->writersWaiting > 0 ) { + ReleaseOneBasicQueue ( this->writerQueue ); + } else if ( this->readersWaiting > 0 ) { + ReleaseAllBasicQueue ( this->readerQueue ); + } + + } + + // ============================================================================================= + + void XMP_HomeGrownLock::ReleaseFromWrite() + { + XMP_AutoMutex autoMutex ( &this->queueMutex ); + + XMP_Assert ( this->beingWritten && (this->lockCount == 1) ); + --this->lockCount; // ! Does not need atomic decrement, protected by queue mutex. + this->beingWritten = false; + + if ( this->writersWaiting > 0 ) { + ReleaseOneBasicQueue ( this->writerQueue ); + } else if ( this->readersWaiting > 0 ) { + ReleaseAllBasicQueue ( this->readerQueue ); + } + } + + // ============================================================================================= + +#endif + +// ================================================================================================= +// Data structure dumping utilities +// ================================ + +void +DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, void * refCon ) +{ + + char buffer [20]; + bool prevNormal; + XMP_Status status = 0; + + XMP_StringPtr spanStart, spanEnd; + XMP_StringPtr valueEnd = &value[0] + value.size(); + + spanStart = &value[0]; + while ( spanStart < valueEnd ) { + + // Output the next span of regular characters. + for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) { + if ( *spanEnd > 0x7F ) break; + if ( (*spanEnd < 0x20) && (*spanEnd != kTab) && (*spanEnd != kLF) ) break; + } + if ( spanStart != spanEnd ) status = (*outProc) ( refCon, spanStart, (XMP_StringLen)(spanEnd-spanStart) ); + if ( status != 0 ) break; + spanStart = spanEnd; + + // Output the next span of irregular characters. + prevNormal = true; + for ( spanEnd = spanStart; spanEnd < valueEnd; ++spanEnd ) { + if ( ((0x20 <= *spanEnd) && (*spanEnd <= 0x7F)) || (*spanEnd == kTab) || (*spanEnd == kLF) ) break; + char space = ' '; + if ( prevNormal ) space = '<'; + status = (*outProc) ( refCon, &space, 1 ); + if ( status != 0 ) break; + OutProcHexByte ( *spanEnd ); + prevNormal = false; + } + if ( ! prevNormal ) { + status = (*outProc) ( refCon, ">", 1 ); + if ( status != 0 ) return; + } + spanStart = spanEnd; + + } + +} // DumpClearString + +// ------------------------------------------------------------------------------------------------- + +static void +DumpStringMap ( const XMP_StringMap & map, XMP_StringPtr label, XMP_TextOutputProc outProc, void * refCon ) +{ + XMP_cStringMapPos currPos; + XMP_cStringMapPos endPos = map.end(); + + size_t maxLen = 0; + for ( currPos = map.begin(); currPos != endPos; ++currPos ) { + size_t currLen = currPos->first.size(); + if ( currLen > maxLen ) maxLen = currLen; + } + + OutProcNewline(); + OutProcLiteral ( label ); + OutProcNewline(); + + for ( currPos = map.begin(); currPos != endPos; ++currPos ) { + OutProcNChars ( " ", 2 ); + DumpClearString ( currPos->first, outProc, refCon ); + OutProcPadding ( maxLen - currPos->first.size() ); + OutProcNChars ( " => ", 4 ); + DumpClearString ( currPos->second, outProc, refCon ); + OutProcNewline(); + } + +} // DumpStringMap + +// ================================================================================================= +// Namespace Tables +// ================================================================================================= + +XMP_NamespaceTable::XMP_NamespaceTable ( const XMP_NamespaceTable & presets ) +{ + XMP_AutoLock presetLock ( &presets.lock, kXMP_ReadLock ); + + this->uriToPrefixMap = presets.uriToPrefixMap; + this->prefixToURIMap = presets.prefixToURIMap; + +} // XMP_NamespaceTable::XMP_NamespaceTable + +// ================================================================================================= + +bool XMP_NamespaceTable::Define ( XMP_StringPtr _uri, XMP_StringPtr _suggPrefix, + XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) +{ + XMP_AutoLock tableLock ( &this->lock, kXMP_WriteLock ); + bool prefixMatches = false; + + XMP_Assert ( (_uri != 0) && (*_uri != 0) && (_suggPrefix != 0) && (*_suggPrefix != 0) ); + + XMP_VarString uri ( _uri ); + XMP_VarString suggPrefix ( _suggPrefix ); + if ( suggPrefix[suggPrefix.size()-1] != ':' ) suggPrefix += ':'; + VerifySimpleXMLName ( _suggPrefix, _suggPrefix+suggPrefix.size()-1 ); // Exclude the colon. + + XMP_StringMapPos uriPos = this->uriToPrefixMap.find ( uri ); + + if ( uriPos == this->uriToPrefixMap.end() ) { + + // The URI is not yet registered, make sure we use a unique prefix. + + XMP_VarString uniqPrefix ( suggPrefix ); + int suffix = 0; + char buffer [32]; // AUDIT: Plenty of room for the "_%d_" suffix. + + while ( true ) { + if ( this->prefixToURIMap.find ( uniqPrefix ) == this->prefixToURIMap.end() ) break; + ++suffix; + snprintf ( buffer, sizeof(buffer), "_%d_:", suffix ); // AUDIT: Using sizeof for snprintf length is safe. + uniqPrefix = suggPrefix; + uniqPrefix.erase ( uniqPrefix.size()-1 ); // ! Remove the trailing ':'. + uniqPrefix += buffer; + } + + // Add the new namespace to both maps. + + XMP_StringPair newNS ( uri, uniqPrefix ); + uriPos = this->uriToPrefixMap.insert ( this->uriToPrefixMap.end(), newNS ); + + newNS.first.swap ( newNS.second ); + (void) this->prefixToURIMap.insert ( this->prefixToURIMap.end(), newNS ); + + } + + // Return the actual prefix and see if it matches the suggested prefix. + + if ( prefixPtr != 0 ) *prefixPtr = uriPos->second.c_str(); + if ( prefixLen != 0 ) *prefixLen = (XMP_StringLen)uriPos->second.size(); + + prefixMatches = ( uriPos->second == suggPrefix ); + return prefixMatches; + +} // XMP_NamespaceTable::Define + +// ================================================================================================= + +bool XMP_NamespaceTable::GetPrefix ( XMP_StringPtr _uri, XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) const +{ + XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock ); + bool found = false; + + XMP_Assert ( (_uri != 0) && (*_uri != 0) ); + + XMP_VarString uri ( _uri ); + XMP_cStringMapPos uriPos = this->uriToPrefixMap.find ( uri ); + + if ( uriPos != this->uriToPrefixMap.end() ) { + if ( prefixPtr != 0 ) *prefixPtr = uriPos->second.c_str(); + if ( prefixLen != 0 ) *prefixLen = (XMP_StringLen)uriPos->second.size(); + found = true; + } + + return found; + +} // XMP_NamespaceTable::GetPrefix + +// ================================================================================================= + +bool XMP_NamespaceTable::GetURI ( XMP_StringPtr _prefix, XMP_StringPtr * uriPtr, XMP_StringLen * uriLen ) const +{ + XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock ); + + bool found = false; + + XMP_Assert ( (_prefix != 0) && (*_prefix != 0) ); + + XMP_VarString prefix ( _prefix ); + if ( prefix[prefix.size()-1] != ':' ) prefix += ':'; + XMP_cStringMapPos prefixPos = this->prefixToURIMap.find ( prefix ); + + if ( prefixPos != this->prefixToURIMap.end() ) { + if ( uriPtr != 0 ) *uriPtr = prefixPos->second.c_str(); + if ( uriLen != 0 ) *uriLen = (XMP_StringLen)prefixPos->second.size(); + found = true; + } + + return found; + +} // XMP_NamespaceTable::GetURI + +// ================================================================================================= + +void XMP_NamespaceTable::Dump ( XMP_TextOutputProc outProc, void * refCon ) const +{ + XMP_AutoLock tableLock ( &this->lock, kXMP_ReadLock ); + + XMP_cStringMapPos p2uEnd = this->prefixToURIMap.end(); // ! Move up to avoid gcc complaints. + XMP_cStringMapPos u2pEnd = this->uriToPrefixMap.end(); + + DumpStringMap ( this->prefixToURIMap, "Dumping namespace prefix to URI map", outProc, refCon ); + + if ( this->prefixToURIMap.size() != this->uriToPrefixMap.size() ) { + OutProcLiteral ( "** bad namespace map sizes **" ); + XMP_Throw ( "Fatal namespace map problem", kXMPErr_InternalFailure ); + } + + for ( XMP_cStringMapPos nsLeft = this->prefixToURIMap.begin(); nsLeft != p2uEnd; ++nsLeft ) { + + XMP_cStringMapPos nsOther = this->uriToPrefixMap.find ( nsLeft->second ); + if ( (nsOther == u2pEnd) || (nsLeft != this->prefixToURIMap.find ( nsOther->second )) ) { + OutProcLiteral ( " ** bad namespace URI ** " ); + DumpClearString ( nsLeft->second, outProc, refCon ); + break; + } + + for ( XMP_cStringMapPos nsRight = nsLeft; nsRight != p2uEnd; ++nsRight ) { + if ( nsRight == nsLeft ) continue; // ! Can't start at nsLeft+1, no operator+! + if ( nsLeft->second == nsRight->second ) { + OutProcLiteral ( " ** duplicate namespace URI ** " ); + DumpClearString ( nsLeft->second, outProc, refCon ); + break; + } + } + + } + + for ( XMP_cStringMapPos nsLeft = this->uriToPrefixMap.begin(); nsLeft != u2pEnd; ++nsLeft ) { + + XMP_cStringMapPos nsOther = this->prefixToURIMap.find ( nsLeft->second ); + if ( (nsOther == p2uEnd) || (nsLeft != this->uriToPrefixMap.find ( nsOther->second )) ) { + OutProcLiteral ( " ** bad namespace prefix ** " ); + DumpClearString ( nsLeft->second, outProc, refCon ); + break; + } + + for ( XMP_cStringMapPos nsRight = nsLeft; nsRight != u2pEnd; ++nsRight ) { + if ( nsRight == nsLeft ) continue; // ! Can't start at nsLeft+1, no operator+! + if ( nsLeft->second == nsRight->second ) { + OutProcLiteral ( " ** duplicate namespace prefix ** " ); + DumpClearString ( nsLeft->second, outProc, refCon ); + break; + } + } + + } + +} // XMP_NamespaceTable::Dump + +// ================================================================================================= +static XMP_Bool matchdigit ( XMP_StringPtr text ) { + if ( *text >= '0' && *text <= '9' ) + return true; + return false; +} + +static XMP_Bool matchUpperCase ( XMP_StringPtr text ) { + if ( *text >= 'A' && *text <= 'Z' ) + return true; + return false; +} + +static XMP_Bool matchLowerCase ( XMP_StringPtr text ) { + if ( *text >= 'a' && *text <= 'z' ) + return true; + return false; +} + +/* matchhere: search for regexp at beginning of text */ +static XMP_Bool matchhere ( XMP_StringPtr regexp, XMP_StringPtr text ) { + if ( regexp[0] == '\0' ) + return true; + if ( regexp[0] == '\\' ) { + if ( regexp[1] == 'd' ) { + if ( matchdigit(text) ) + return matchhere ( regexp+2, text+1 ); + else + return false; + } + else if ( regexp[1] == 'W' ) { + if ( matchUpperCase(text) ) + return matchhere ( regexp+2, text+1 ); + else + return false; + } + else if ( regexp[1] == 'w' ) { + if ( matchLowerCase(text) ) + return matchhere ( regexp+2, text+1 ); + else + return false; + } + } + + if ( regexp[0] == '$' && regexp[1] == '\0' ) + return *text == '\0'; + + if ( *text != '\0' && regexp[0] == *text ) + return matchhere ( regexp+1, text+1 ); + return 0; +} + +/* match: search for regexp anywhere in text */ +static XMP_Bool match ( XMP_StringPtr regexp, XMP_StringPtr text ) { + if ( regexp[0] == '^' ) + return matchhere ( regexp+1, text ); + do { /* must look even if string is empty */ + if ( matchhere ( regexp, text ) ) + return true; + } while ( *text++ != '\0' ); + return false; +} + +XMP_Bool XMP_RegExp::Match ( XMP_StringPtr s ) +{ + if ( regExpStr.size() == 0 ) + return true; + if ( s == NULL ) + return false; + return match ( this->regExpStr.c_str(), s ); +} +// ================================================================================================= diff --git a/source/lib/xmp_core/XMP_LibUtils.hpp b/source/lib/xmp_core/XMP_LibUtils.hpp new file mode 100644 index 0000000..b7a9e8f --- /dev/null +++ b/source/lib/xmp_core/XMP_LibUtils.hpp @@ -0,0 +1,619 @@ +#ifndef __XMP_LibUtils_hpp__ +#define __XMP_LibUtils_hpp__ 1 + +// ================================================================================================= +// Copyright 2009 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "public/include/XMP_Environment.h" // ! Must be the first include. +#include "public/include/XMP_Const.h" + +#include +#include +#include + +#if XMP_DebugBuild + #include +#endif + +#if XMP_WinBuild + #ifndef snprintf + #define snprintf _snprintf + #endif +#endif + +// ================================================================================================= +// Basic types, constants +// ====================== + +#define kTab ((char)0x09) +#define kLF ((char)0x0A) +#define kCR ((char)0x0D) + +#if XMP_WinBuild + #define kDirChar '\\' +#else + #define kDirChar '/' +#endif + +typedef std::string XMP_VarString; + +#define EliminateGlobal(g) delete ( g ); g = 0 + +extern "C" bool Initialize_LibUtils(); +extern "C" void Terminate_LibUtils(); + +#define IgnoreParam(p) (void)p + +// The builtin offsetof macro sometimes violates C++ data member rules. +#define XMP_OffsetOf(struct,field) ( (char*)(&((struct*)0x100)->field) - (char*)0x100 ) + +// ================================================================================================= +// Support for exceptions and asserts +// ================================== + +#define AnnounceThrow(msg) /* Do nothing. */ +#define AnnounceCatch(msg) /* Do nothing. */ + +#define XMP_Throw(msg,id) { AnnounceThrow ( msg ); throw XMP_Error ( id, msg ); } + +#if XMP_DebugBuild +#define XMP_Throw_Verbose(msg,e,id) \ +{ \ + char tmpMsg[255]; \ + snprintf(tmpMsg, sizeof(tmpMsg), #msg "( %d )", e); \ + XMP_Throw( tmpMsg, id); \ +} +#else + #define XMP_Throw_Verbose(msg,e,id) XMP_Throw(msg, id) +#endif + +class GenericErrorCallback { +public: + // Abstract base class for XMPCore and XMPFiles internal error notification support. Needed so + // that the XMLParserAdapter (used by both XMPCore and XMPFiles) can send error notifications, + // and so that utility parts of just XMPCore or XMPFiles can avoid dependence on XMPCore.hpp or + // XMPFiles.hpp if that is appropriate. + + XMP_Uns32 limit; + mutable XMP_Uns32 notifications; + mutable XMP_ErrorSeverity topSeverity; + + GenericErrorCallback() : notifications(0), limit(1), topSeverity(kXMPErrSev_Recoverable) {}; + virtual ~GenericErrorCallback() {}; + + void Clear() { this->notifications = 0; this->limit = 1; this->topSeverity = kXMPErrSev_Recoverable; }; + + bool CheckLimitAndSeverity (XMP_ErrorSeverity severity ) const; + + // Const so they can be used with const XMPMeta and XMPFiles objects. + void NotifyClient ( XMP_ErrorSeverity severity, XMP_Error & error, XMP_StringPtr filePath = 0 ) const; + + virtual bool CanNotify ( ) const = 0; + virtual bool ClientCallbackWrapper ( XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr messsage ) const = 0; + +}; + +#define XMP_Error_Throw(error) { AnnounceThrow (error.GetErrMsg()); throw error; } + + +// ------------------------------------------------------------------------------------------------- + +#define _MakeStr(p) #p +#define _NotifyMsg(n,c,f,l) #n " failed: " #c " in " f " at line " _MakeStr(l) +#define _ExplicitMsg(msg,c,e) #e " " #msg ": " #c + +#define XMP_Validate(c,msg,e) \ + if ( ! (c) ) { \ + const char * validate_msg = _ExplicitMsg ( msg, c, e ); \ + XMP_Throw ( validate_msg, e ); \ + } + +// This statement is needed in XMP_Assert definition to reduce warnings from +// static analysis tool in Visual Studio. Defined here, as platform fork not +// possible within macro definition below +#if XMP_WinBuild + #define analysis_assume(c) __analysis_assume( c ); +#else + #define analysis_assume(c) ((void) 0) +#endif + +#if ! XMP_DebugBuild + #define XMP_Assert(c) ((void) 0) +#else + #define XMP_Assert(c) assert ( c ) +#endif + + #define XMP_Enforce(c) \ + if ( ! (c) ) { \ + const char * assert_msg = _NotifyMsg ( XMP_Enforce, (c), __FILE__, __LINE__ ); \ + XMP_Throw ( assert_msg , kXMPErr_EnforceFailure ); \ + } +// ================================================================================================= +// Thread synchronization locks +// ============================ + +// About XMP and thread synchronization +// +// A variety of choices are provided for thread synchronization. Exactly one method must be chosen +// by defining the appropriate symbol to 1. +// +// * UseNoLock - This choice turns the synchronization functions into no-ops. It must only be used +// by single threaded clients, or clients providing their own control at a higher level. +// +// * UseGlobalLibraryLock - This choice uses a single per-library lock. The result is thread safe +// but unfriendly behavior, no true concurrency. This should only be used as a debugging fallback. +// +// * UseBoostLock - This choice uses the Boost shared_mutex mechanism. It has the advantage of being +// robust and being available on pretty much all platforms. It has the disadvantage of requiring +// the developer to download, integrate, and build the Boost thread library. +// +// * UsePThreadLock - This choice uses the POSIX pthread rwlock mechanism. It has the advantage of +// being robust and being available on any modern UNIX platform, including Mac OS X. +// +// * UseWinSlimLock - This choice uses the Windows slim reader/writer mechanism. It is robust but +// only available on Vista and newer versions of Windows, it is not available on XP. +// +// * UseHomeGrownLock - This choice uses local code plus lower level synchronization primitives. It +// has the advantage of being usable on all platforms, and having exposed and tunable policy. It +// has the disadvantage of possibly being less robust than Boost or the O/S provided mechanisms. +// The lower level synchronization primitives are pthread mutex and condition for UNIX (including +// Mac OS X). For Windows there is a choice of critical section and condition variable for Vista +// and newer; or critical section, event, and semaphore for XP and newer. + +#define UseNoLock 1 + +// ------------------------------------------------------------------------------------------------- +// A basic exclusive access mutex and atomic increment/decrement operations. + +#if XMP_WinBuild + + #include + + #define HaveAtomicIncrDecr 1 + typedef LONG XMP_AtomicCounter; + + #define XMP_AtomicIncrement(x) InterlockedIncrement ( &(x) ) + #define XMP_AtomicDecrement(x) InterlockedDecrement ( &(x) ) + + typedef CRITICAL_SECTION XMP_BasicMutex; + + #define InitializeBasicMutex(mutex) { InitializeCriticalSection ( &mutex ); } + #define TerminateBasicMutex(mutex) { DeleteCriticalSection ( &mutex ); } + #define AcquireBasicMutex(mutex) { EnterCriticalSection ( &mutex ); } + #define ReleaseBasicMutex(mutex) { LeaveCriticalSection ( &mutex ); } + +#elif XMP_MacBuild | XMP_iOSBuild + + #include + #include + + #define HaveAtomicIncrDecr 1 + typedef int32_t XMP_AtomicCounter; + + #define XMP_AtomicIncrement(x) OSAtomicIncrement32 ( &(x) ) + #define XMP_AtomicDecrement(x) OSAtomicDecrement32 ( &(x) ) + + typedef pthread_mutex_t XMP_BasicMutex; + + #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); } + #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); } + #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); } + #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); } + +#elif XMP_UNIXBuild + + #include + + // Atomic increment/decrement intrinsics should be in gcc 4.1, but Solaris seems to lack them. + #ifndef HaveAtomicIncrDecr + #define HaveAtomicIncrDecr 1 + #endif + #if HaveAtomicIncrDecr + typedef XMP_Uns32 XMP_AtomicCounter; + #define XMP_AtomicIncrement(x) __sync_add_and_fetch ( &(x), 1 ) + #define XMP_AtomicDecrement(x) __sync_sub_and_fetch ( &(x), 1 ) + #endif + + typedef pthread_mutex_t XMP_BasicMutex; + + #define InitializeBasicMutex(mutex) { int err = pthread_mutex_init ( &mutex, 0 ); XMP_Enforce ( err == 0 ); } + #define TerminateBasicMutex(mutex) { int err = pthread_mutex_destroy ( &mutex ); XMP_Enforce ( err == 0 ); } + #define AcquireBasicMutex(mutex) { int err = pthread_mutex_lock ( &mutex ); XMP_Enforce ( err == 0 ); } + #define ReleaseBasicMutex(mutex) { int err = pthread_mutex_unlock ( &mutex ); XMP_Enforce ( err == 0 ); } + +#elif XMP_BanzaiBuild + + #include + + // Atomic increment/decrement intrinsics should be in gcc 4.1, but Solaris seems to lack them. + #ifndef HaveAtomicIncrDecr + #define HaveAtomicIncrDecr 1 + #endif + #if HaveAtomicIncrDecr + typedef XMP_Uns32 XMP_AtomicCounter; + #define XMP_AtomicIncrement(x) __sync_add_and_fetch ( &(x), 1 ) + #define XMP_AtomicDecrement(x) __sync_sub_and_fetch ( &(x), 1 ) + #endif + + typedef rtos_mutex_t XMP_BasicMutex; + + #define InitializeBasicMutex(mutex) { int err = rtos_mutex_create ( &mutex, "mutex"); XMP_Enforce ( err == 0 ); } + #define TerminateBasicMutex(mutex) { int err = rtos_mutex_delete ( &mutex ); XMP_Enforce ( err == 0 ); } + #define AcquireBasicMutex(mutex) { int err = rtos_mutex_acquire ( &mutex, RTOS_WAIT_FOREVER ); XMP_Enforce ( err == 0 ); } + #define ReleaseBasicMutex(mutex) { int err = rtos_mutex_release ( &mutex ); XMP_Enforce ( err == 0 ); } + +#endif + +class XMP_AutoMutex { +public: + XMP_AutoMutex ( XMP_BasicMutex * _mutex ) : mutex(_mutex) { AcquireBasicMutex ( *this->mutex ); } + ~XMP_AutoMutex() { this->Release(); } + void Release() { if ( this->mutex != 0 ) ReleaseBasicMutex ( *this->mutex ); this->mutex = 0; } +private: + XMP_BasicMutex * mutex; + XMP_AutoMutex() {}; // ! Must not be used. +}; + +// ------------------------------------------------------------------------------------------------- +// Details for the various locking mechanisms. + +#if UseNoLock + + typedef void* XMP_BasicRWLock; // For single threaded clients that want maximum performance. + + #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */ + #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */ + + #define XMP_BasicRWLock_AcquireForRead(lck) /* Do nothing. */ + #define XMP_BasicRWLock_AcquireForWrite(lck) /* Do nothing. */ + + #define XMP_BasicRWLock_ReleaseFromRead(lck) /* Do nothing. */ + #define XMP_BasicRWLock_ReleaseFromWrite(lck) /* Do nothing. */ + +#elif UseGlobalLibraryLock + + extern XMP_BasicMutex sLibraryLock; + + typedef void* XMP_BasicRWLock; // Use the old thread-unfriendly per-DLL mutex. + + #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */ + #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */ + + #define XMP_BasicRWLock_AcquireForRead(lck) /* Do nothing. */ + #define XMP_BasicRWLock_AcquireForWrite(lck) /* Do nothing. */ + + #define XMP_BasicRWLock_ReleaseFromRead(lck) /* Do nothing. */ + #define XMP_BasicRWLock_ReleaseFromWrite(lck) /* Do nothing. */ + +#elif UseBoostLock + + #include + typedef boost::shared_mutex XMP_BasicRWLock; + + #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */ + #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */ + + #define XMP_BasicRWLock_AcquireForRead(lck) lck.lock_shared() + #define XMP_BasicRWLock_AcquireForWrite(lck) lck.lock() + + #define XMP_BasicRWLock_ReleaseFromRead(lck) lck.unlock_shared() + #define XMP_BasicRWLock_ReleaseFromWrite(lck) lck.unlock() + +#elif UsePThreadLock + + #include + typedef pthread_rwlock_t XMP_BasicRWLock; + + #define XMP_BasicRWLock_Initialize(lck) \ + { int err = pthread_rwlock_init ( &lck, 0 ); \ + if ( err != 0 ) XMP_Throw ( "Initialize pthread rwlock failed", kXMPErr_ExternalFailure ); } + #define XMP_BasicRWLock_Terminate(lck) \ + { int err = pthread_rwlock_destroy ( &lck ); XMP_Assert ( err == 0 ); } + + #define XMP_BasicRWLock_AcquireForRead(lck) \ + { int err = pthread_rwlock_rdlock ( &lck ); \ + if ( err != 0 ) XMP_Throw ( "Acquire pthread read lock failed", kXMPErr_ExternalFailure ); } + #define XMP_BasicRWLock_AcquireForWrite(lck) \ + { int err = pthread_rwlock_wrlock ( &lck ); \ + if ( err != 0 ) XMP_Throw ( "Acquire pthread write lock failed", kXMPErr_ExternalFailure ); } + + #define XMP_BasicRWLock_ReleaseFromRead(lck) \ + { int err = pthread_rwlock_unlock ( &lck ); \ + if ( err != 0 ) XMP_Throw ( "Release pthread read lock failed", kXMPErr_ExternalFailure ); } + #define XMP_BasicRWLock_ReleaseFromWrite(lck) \ + { int err = pthread_rwlock_unlock ( &lck ); \ + if ( err != 0 ) XMP_Throw ( "Release pthread write lock failed", kXMPErr_ExternalFailure ); } + +#elif UseWinSlimLock + + #include + typedef SRWLOCK XMP_BasicRWLock; + + #define XMP_BasicRWLock_Initialize(lck) InitializeSRWLock ( &lck ) + #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */ + + #define XMP_BasicRWLock_AcquireForRead(lck) AcquireSRWLockShared ( &lck ) + #define XMP_BasicRWLock_AcquireForWrite(lck) AcquireSRWLockExclusive ( &lck ) + + #define XMP_BasicRWLock_ReleaseFromRead(lck) ReleaseSRWLockShared ( &lck ) + #define XMP_BasicRWLock_ReleaseFromWrite(lck) ReleaseSRWLockExclusive ( &lck ) + +#elif UseHomeGrownLock + + class XMP_HomeGrownLock; + typedef XMP_HomeGrownLock XMP_BasicRWLock; + + #define XMP_BasicRWLock_Initialize(lck) /* Do nothing. */ + #define XMP_BasicRWLock_Terminate(lck) /* Do nothing. */ + #define XMP_BasicRWLock_AcquireForRead(lck) lck.AcquireForRead() + #define XMP_BasicRWLock_AcquireForWrite(lck) lck.AcquireForWrite() + #define XMP_BasicRWLock_ReleaseFromRead(lck) lck.ReleaseFromRead() + #define XMP_BasicRWLock_ReleaseFromWrite(lck) lck.ReleaseFromWrite() + + #if XMP_MacBuild | XMP_UNIXBuild | XMP_iOSBuild + + #include + + typedef pthread_cond_t XMP_BasicQueue; + + #elif XMP_WinBuild + + #include + #ifndef BuildLocksForWinXP + #define BuildLocksForWinXP 1 + #endif + + #if ! BuildLocksForWinXP + typedef CONDITION_VARIABLE XMP_BasicQueue; // ! Requires Vista or newer. + #else + class XMP_WinXP_HGQueue { + public: + XMP_WinXP_HGQueue(); + ~XMP_WinXP_HGQueue(); + void Wait ( XMP_BasicMutex & queueMutex ); + void ReleaseOne(); + void ReleaseAll(); + private: + HANDLE queueEvent; + volatile XMP_Uns32 waitCount; // ! Does not need to be XMP_AtomicCounter. + volatile bool releaseAll; + }; + typedef XMP_WinXP_HGQueue XMP_BasicQueue; + #endif + + #endif + + class XMP_HomeGrownLock { + public: + XMP_HomeGrownLock(); + ~XMP_HomeGrownLock(); + void AcquireForRead(); + void AcquireForWrite(); + void ReleaseFromRead(); + void ReleaseFromWrite(); + private: + XMP_BasicMutex queueMutex; // Used to protect queueing operations. + XMP_BasicQueue readerQueue, writerQueue; + volatile XMP_Uns32 lockCount, readersWaiting, writersWaiting; // ! Does not need to be XMP_AtomicCounter. + volatile bool beingWritten; + }; + +#else + + #error "No locking mechanism chosen" + +#endif + +class XMP_ReadWriteLock { // For the lock objects, use XMP_AutoLock to do the locking. +public: + XMP_ReadWriteLock(); + ~XMP_ReadWriteLock(); + void Acquire ( bool forWriting ); + void Release(); +private: + XMP_BasicRWLock lock; + #if XMP_DebugBuild && HaveAtomicIncrDecr + volatile XMP_AtomicCounter lockCount; // ! Only for debug checks, must be XMP_AtomicCounter. + #endif + volatile bool beingWritten; +}; + +#define kXMP_ReadLock false +#define kXMP_WriteLock true + +class XMP_AutoLock { +public: + XMP_AutoLock ( const XMP_ReadWriteLock * _lock, bool forWriting, bool cond = true ) : lock(0) + { + if ( cond ) { + // The cast below is needed because the _lock parameter might come from something + // like "const XMPMeta &", which would make the lock itself const. But we need to + // modify the lock (to acquire and release) even if the owning object is const. + this->lock = (XMP_ReadWriteLock*)_lock; + this->lock->Acquire ( forWriting ); + } + } + ~XMP_AutoLock() { this->Release(); } + void Release() { if ( this->lock != 0 ) this->lock->Release(); this->lock = 0; } +private: + XMP_ReadWriteLock * lock; + XMP_AutoLock() {}; // ! Must not be used. +}; + +// ================================================================================================= +// Support for wrappers +// ==================== + +#define AnnounceStaticEntry(proc) /* Do nothing. */ +#define AnnounceObjectEntry(proc,rwMode) /* Do nothing. */ + +#define AnnounceExit() /* Do nothing. */ + +// ------------------------------------------------------------------------------------------------- + +#if UseGlobalLibraryLock + #define AcquireLibraryLock(lck) XMP_AutoMutex libLock ( &lck ) +#else + #define AcquireLibraryLock(lck) /* nothing */ +#endif + +#define XMP_ENTER_NoLock(Proc) \ + AnnounceStaticEntry ( Proc ); \ + try { \ + wResult->errMessage = 0; + +#define XMP_ENTER_Static(Proc) \ + AnnounceStaticEntry ( Proc ); \ + AcquireLibraryLock ( sLibraryLock ); \ + try { \ + wResult->errMessage = 0; + +#define XMP_ENTER_ObjRead(XMPClass,Proc) \ + AnnounceObjectEntry ( Proc, "reader" ); \ + AcquireLibraryLock ( sLibraryLock ); \ + const XMPClass & thiz = *((XMPClass*)xmpObjRef); \ + XMP_AutoLock objLock ( &thiz.lock, kXMP_ReadLock ); \ + try { \ + wResult->errMessage = 0; + +#define XMP_ENTER_ObjWrite(XMPClass,Proc) \ + AnnounceObjectEntry ( Proc, "writer" ); \ + AcquireLibraryLock ( sLibraryLock ); \ + XMPClass * thiz = (XMPClass*)xmpObjRef; \ + XMP_AutoLock objLock ( &thiz->lock, kXMP_WriteLock ); \ + try { \ + wResult->errMessage = 0; + +#define XMP_EXIT \ + XMP_CATCH_EXCEPTIONS \ + AnnounceExit(); + +#define XMP_EXIT_NoThrow \ + } catch ( ... ) { \ + AnnounceCatch ( "no-throw catch-all" ); \ + /* Do nothing. */ \ + } \ + AnnounceExit(); + +#define XMP_CATCH_EXCEPTIONS \ + } catch ( XMP_Error & xmpErr ) { \ + wResult->int32Result = xmpErr.GetID(); \ + wResult->ptrResult = (void*)"XMP"; \ + wResult->errMessage = xmpErr.GetErrMsg(); \ + if ( wResult->errMessage == 0 ) wResult->errMessage = ""; \ + AnnounceCatch ( wResult->errMessage ); \ + } catch ( std::exception & stdErr ) { \ + wResult->int32Result = kXMPErr_StdException; \ + wResult->errMessage = stdErr.what(); \ + if ( wResult->errMessage == 0 ) wResult->errMessage = ""; \ + AnnounceCatch ( wResult->errMessage ); \ + } catch ( ... ) { \ + wResult->int32Result = kXMPErr_UnknownException; \ + wResult->errMessage = "Caught unknown exception"; \ + AnnounceCatch ( wResult->errMessage ); \ + } + +#if XMP_DebugBuild + #define RELEASE_NO_THROW /* empty */ +#else + #define RELEASE_NO_THROW throw() +#endif + +// ================================================================================================= +// Data structure dumping utilities +// ================================ + +#define IsHexDigit(ch) ( (('0' <= (ch)) && ((ch) <= '9')) || (('A' <= (ch)) && ((ch) <= 'F')) ) +#define HexDigitValue(ch) ( (((ch) - '0') < 10) ? ((ch) - '0') : ((ch) - 'A' + 10) ) + +static const char * kTenSpaces = " "; +#define OutProcPadding(pad) { size_t padLen = (pad); \ + for ( ; padLen >= 10; padLen -= 10 ) OutProcNChars ( kTenSpaces, 10 ); \ + for ( ; padLen > 0; padLen -= 1 ) OutProcNChars ( " ", 1 ); } + + +#define OutProcNewline() { XMP_Status status = (*outProc) ( refCon, "\n", 1 ); if ( status != 0 ) return; } + +#define OutProcNChars(p,n) { XMP_Status status = (*outProc) ( refCon, (p), (n) ); if ( status != 0 ) return; } + +#define OutProcLiteral(lit) { XMP_Status _status = (*outProc) ( refCon, (lit), (XMP_StringLen)strlen(lit) ); if ( _status != 0 ) return; } + +#define OutProcString(str) { XMP_Status _status = (*outProc) ( refCon, (str).c_str(), (XMP_StringLen)(str).size() ); if ( _status != 0 ) return; } + +#define OutProcDecInt(num) { snprintf ( buffer, sizeof(buffer), "%ld", (long)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \ + buffer[sizeof(buffer) -1] = 0; /* AUDIT warning C6053: Make sure buffer is terminated */ \ + XMP_Status _status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( _status != 0 ) return; } + +#define OutProcHexInt(num) { snprintf ( buffer, sizeof(buffer), "%lX", (long)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \ + buffer[sizeof(buffer) -1] = 0; /* AUDIT warning C6053: Make sure buffer is terminated */ \ + XMP_Status _status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( _status != 0 ) return; } + +#define OutProcHexByte(num) { snprintf ( buffer, sizeof(buffer), "%.2X", (unsigned char)(num) ); /* AUDIT: Using sizeof for snprintf length is safe */ \ + XMP_Status _status = (*outProc) ( refCon, buffer, (XMP_StringLen)strlen(buffer) ); if ( _status != 0 ) return; } + +static const char * kIndent = " "; +#define OutProcIndent(lev) { for ( size_t i = 0; i < (lev); ++i ) OutProcNChars ( kIndent, 3 ); } + +void DumpClearString ( const XMP_VarString & value, XMP_TextOutputProc outProc, void * refCon ); + +// ================================================================================================= +// Namespace Tables +// ================ +typedef std::vector XMP_StringVector; +typedef XMP_StringVector::iterator XMP_StringVectorPos; +typedef XMP_StringVector::const_iterator XMP_StringVectorCPos; + +typedef std::pair < XMP_VarString, XMP_VarString > XMP_StringPair; + +typedef std::map < XMP_VarString, XMP_VarString > XMP_StringMap; +typedef XMP_StringMap::iterator XMP_StringMapPos; +typedef XMP_StringMap::const_iterator XMP_cStringMapPos; + +class XMP_NamespaceTable { +public: + + XMP_NamespaceTable() {}; + XMP_NamespaceTable ( const XMP_NamespaceTable & presets ); + virtual ~XMP_NamespaceTable() {}; + + bool Define ( XMP_StringPtr uri, XMP_StringPtr suggPrefix, + XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen); + + bool GetPrefix ( XMP_StringPtr uri, XMP_StringPtr * prefixPtr, XMP_StringLen * prefixLen ) const; + bool GetURI ( XMP_StringPtr prefix, XMP_StringPtr * uriPtr, XMP_StringLen * uriLen ) const; + + void Dump ( XMP_TextOutputProc outProc, void * refCon ) const; + +private: + + XMP_ReadWriteLock lock; + XMP_StringMap uriToPrefixMap, prefixToURIMap; + +}; + + +// Right now it supports only ^, $ and \d, in future we should use it as a wrapper over +// regex object once mac and Linux compilers start supporting them. + +class XMP_RegExp { +public: + XMP_RegExp ( XMP_StringPtr regExp ) + { + if ( regExp ) + regExpStr = regExp; + } + + XMP_Bool Match ( XMP_StringPtr s ); + +private: + XMP_VarString regExpStr; +}; + +// ================================================================================================= + +#endif // __XMP_LibUtils_hpp__ diff --git a/source/lib/xmp_core/public/include/TXMPFiles.hpp b/source/lib/xmp_core/public/include/TXMPFiles.hpp new file mode 100644 index 0000000..27ee413 --- /dev/null +++ b/source/lib/xmp_core/public/include/TXMPFiles.hpp @@ -0,0 +1,855 @@ +#ifndef __TXMPFiles_hpp__ +#define __TXMPFiles_hpp__ 1 + +#if ( ! __XMP_hpp__ ) + #error "Do not directly include, use XMP.hpp" +#endif + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================= +/// \file TXMPFiles.hpp +/// \brief API for access to the main (document-level) metadata in a file_. +/// +/// The Adobe XMP Toolkit's file handling component, XMPFiles, is a front end to a set of +/// format-specific file handlers that support file I/O for XMP. The file handlers implement smart, +/// efficient support for those file formats for which the means to embed XMP is defined in the XMP +/// Specification. Where possible, this support allows: +/// \li Injection of XMP where none currently exists +/// \li Expansion of XMP without regard to existing padding +/// \li Reconciliation of the XMP and other legacy forms of metadata. +/// +/// \c TXMPFiles is designed for use by clients interested in the metadata and not in the primary +/// file content; the Adobe Bridge application is a typical example. \c TXMPFiles is not intended to +/// be appropriate for files authored by an application; that is, those files for which the +/// application has explicit knowledge of the file format. +// ================================================================================================= + + +// ================================================================================================= +/// \class TXMPFiles TXMPFiles.hpp +/// \brief API for access to the main (document-level) metadata in a file. +/// +/// \c TXMPFiles is a template class that provides the API for the Adobe XMP Toolkit's XMPFiles +/// component. This provides convenient access to the main, or document level, XMP for a file. Use +/// it to obtain metadata from a file, which you can then manipulate with the XMP Core component +/// (the classes \c TXMPMeta, \c TXMPUtils, and \c TXMPIterator); and to write new or changed +/// metadata back out to a file. +/// +/// The functions allow you to open a file, read and write the metadata, then close the file. +/// While open, portions of the file might be maintained in RAM data structures. Memory +/// usage can vary considerably depending onfile format and access options. +/// +/// A file can be opened for read-only or read-write access, with typical exclusion for both +/// modes. Errors result in the throw of an \c XMPError exception. +/// +/// \c TXMPFiles is the template class. It must be instantiated with a string class such as +/// \c std::string. Read the Toolkit Overview for information about the overall architecture of the XMP +/// API, and the documentation for \c XMP.hpp for specific instantiation instructions. +/// +/// Access these functions through the concrete class, \c SXMPFiles. +// ================================================================================================= + + +#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds. + #include "XMP_IO.hpp" +#endif + + +template +class TXMPFiles { + +public: + + // ============================================================================================= + /// \name Initialization and termination + /// @{ + /// + /// A \c TXMPFiles object must be initialized before use and can be terminated when done. + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetVersionInfo() retrieves version information for the XMPFiles component. + /// + /// Can be called before \c #Initialize(). This function is static; make the call directly from + /// the concrete class (\c SXMPFiles). + /// + /// @param versionInfo [out] A buffer in which to return the version information. + + static void GetVersionInfo ( XMP_VersionInfo * versionInfo ); + + // --------------------------------------------------------------------------------------------- + /// @brief Initializes the XMPFiles library; must be called before creating an \c SXMPFiles object. + /// + /// The main action is to activate the available smart file handlers. Must be called before + /// using any methods except \c GetVersionInfo(). + /// + /// This function is static; make the call directly from the concrete class (\c SXMPFiles). + /// + /// @return True on success. + + static bool Initialize(); + + // --------------------------------------------------------------------------------------------- + /// @brief Initializes the XMPFiles library; must be called before creating an \c SXMPFiles object. + /// + /// This overload of TXMPFiles::Initialize() accepts option bits to customize the initialization + /// actions. At this time no option is defined. + /// + /// The main action is to activate the available smart file handlers. Must be called before + /// using any methods except \c GetVersionInfo(). + /// + /// This function is static; make the call directly from the concrete class (\c SXMPFiles). + /// + /// @param options Option flags to control the initialization actions. + /// + /// @return True on success. + + static bool Initialize ( XMP_OptionBits options ); + + // --------------------------------------------------------------------------------------------- + /// @brief Initializes the XMPFiles library; must be called before creating an \c SXMPFiles object. + /// + /// This overload of TXMPFiles::Initialize() accepts plugin directory and name of the plug-ins + /// as a comma separated list to load the file handler plug-ins. If plugins == NULL, then all + /// plug-ins present in the plug-in directory will be loaded. + /// + /// The main action is to activate the available smart file handlers. Must be called before + /// using any methods except \c GetVersionInfo(). + /// + /// This function is static; make the call directly from the concrete class (\c SXMPFiles). + /// + /// @param pluginFolder Pugin directorty to load the file handler plug-ins. + /// @param plugins Comma sepearted list of plug-ins which should be loaded from the plug-in directory. + /// If plugin == NULL, then all plug-ins availbale in the plug-in directory will be loaded. + /// + /// @return True on success. + + static bool Initialize ( const char* pluginFolder, const char* plugins = NULL ); + + // --------------------------------------------------------------------------------------------- + /// @brief Initializes the XMPFiles library; must be called before creating an \c SXMPFiles object. + /// + /// This overload of TXMPFiles::Initialize( XMP_OptionBits options ) accepts plugin directory and + /// name of the plug-ins as a comma separated list to load the file handler plug-ins. + /// If plugins == NULL, then all plug-ins present in the plug-in directory will be loaded. + /// + /// The main action is to activate the available smart file handlers. Must be called before + /// using any methods except \c GetVersionInfo(). + /// + /// This function is static; make the call directly from the concrete class (\c SXMPFiles). + /// + /// @param options Option flags to control the initialization actions. + /// @param pluginFolder Pugin directorty to load the file handler plug-ins. + /// @param plugins Comma sepearted list of plug-ins which should be loaded from the plug-in directory. + /// If plugin == NULL, then all plug-ins availbale in the plug-in directory will be loaded. + /// + /// @return True on success. + + static bool Initialize ( XMP_OptionBits options, const char* pluginFolder, const char* plugins = NULL ); + + // --------------------------------------------------------------------------------------------- + /// @brief Terminates use of the XMPFiles library. + /// + /// Optional. Deallocates global data structures created by intialization. Its main action is to + /// deallocate heap-allocated global storage, for the benefit of client leak checkers. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPFiles). + + static void Terminate(); + + /// @} + + // ============================================================================================= + /// \name Constructors and destructor + /// @{ + /// + /// The default constructor initializes an object that is associated with no file. The alternate + /// constructors call \c OpenFile(). + + // --------------------------------------------------------------------------------------------- + /// @brief Default constructor initializes an object that is associated with no file. + + TXMPFiles(); + + // --------------------------------------------------------------------------------------------- + /// @brief Destructor; typical virtual destructor. + /// + /// The destructor does not call \c CloseFile(); pending updates are lost when the destructor is run. + /// + /// @see \c OpenFile(), \c CloseFile() + + virtual ~TXMPFiles() throw(); + + // --------------------------------------------------------------------------------------------- + /// @brief Alternate constructor associates the new \c XMPFiles object with a specific file. + /// + /// Calls \c OpenFile() to open the specified file after performing a default construct. + /// + /// @param filePath The path for the file, specified as a nul-terminated UTF-8 string. + /// + /// @param format A format hint for the file, if known. + /// + /// @param openFlags Options for how the file is to be opened (for read or read/write, for + /// example). Use a logical OR of these bit-flag constants: + /// + /// \li \c #kXMPFiles_OpenForRead + /// \li \c #kXMPFiles_OpenForUpdate + /// \li \c #kXMPFiles_OpenOnlyXMP + /// \li \c #kXMPFiles_OpenStrictly + /// \li \c #kXMPFiles_OpenUseSmartHandler + /// \li \c #kXMPFiles_OpenUsePacketScanning + /// \li \c #kXMPFiles_OpenLimitedScanning + /// + /// @return The new \c TXMPFiles object. + + TXMPFiles ( XMP_StringPtr filePath, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits openFlags = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief Alternate constructor associates the new \c XMPFiles object with a specific file, + /// using a string object. + /// + /// Overloads the basic form of the function, allowing you to pass a string object + /// for the file path. It is otherwise identical; see details in the canonical form. + + TXMPFiles ( const tStringObj & filePath, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits openFlags = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief Copy constructor + /// + /// Increments an internal reference count but does not perform a deep copy. + /// + /// @param original The existing \c TXMPFiles object to copy. + /// + /// @return The new \c TXMPFiles object. + + TXMPFiles ( const TXMPFiles & original ); + + // --------------------------------------------------------------------------------------------- + /// @brief Assignment operator + /// + /// Increments an internal reference count but does not perform a deep copy. + /// + /// @param rhs The existing \c TXMPFiles object. + + void operator= ( const TXMPFiles & rhs ); + + // --------------------------------------------------------------------------------------------- + /// @brief Reconstructs a \c TXMPFiles object from an internal reference. + /// + /// This constructor creates a new \c TXMPFiles object that refers to the underlying reference + /// object of an existing \c TXMPFiles object. Use to safely pass \c SXMPFiles references across + /// DLL boundaries. + /// + /// @param xmpFilesObj The underlying reference object, obtained from some other XMP object + /// with \c TXMPFiles::GetInternalRef(). + /// + /// @return The new object. + + TXMPFiles ( XMPFilesRef xmpFilesObj ); + + // --------------------------------------------------------------------------------------------- + /// @brief GetInternalRef() retrieves an internal reference that can be safely passed across DLL + /// boundaries and reconstructed. + /// + /// Use with the reconstruction constructor to safely pass \c SXMPFiles references across DLL + /// boundaries where the clients might have used different string types when instantiating + /// \c TXMPFiles. + /// + /// @return The internal reference. + /// + /// @see \c TXMPMeta::GetInternalRef() for usage. + + XMPFilesRef GetInternalRef(); + + /// @} + + // ============================================================================================= + /// \name File handler information + /// @{ + /// + /// Call this static function from the concrete class, \c SXMPFiles, to obtain information about + /// the file handlers for the XMPFiles component. + + // --------------------------------------------------------------------------------------------- + /// @brief GetFormatInfo() reports what features are supported for a specific file format. + /// + /// The file handlers for different file formats vary considerably in what features they + /// support. Support depends on both the general capabilities of the format and the + /// implementation of the handler for that format. + /// + ///This function is static; make the call directly from the concrete class (\c SXMPFiles). + /// + /// @param format The file format whose support flags are desired. + /// + /// @param handlerFlags [out] A buffer in which to return a logical OR of option bit flags. + /// The following constants are defined: + /// + /// \li \c #kXMPFiles_CanInjectXMP - Can inject first-time XMP into an existing file. + /// \li \c #kXMPFiles_CanExpand - Can expand XMP or other metadata in an existing file. + /// \li \c #kXMPFiles_CanRewrite - Can copy one file to another, writing new metadata (as in SaveAs) + /// \li \c #kXMPFiles_CanReconcile - Supports reconciliation between XMP and other forms. + /// \li \c #kXMPFiles_AllowsOnlyXMP - Allows access to just the XMP, ignoring other forms. + /// This is only meaningful if \c #kXMPFiles_CanReconcile is set. + /// \li \c #kXMPFiles_ReturnsRawPacket - File handler returns raw XMP packet information and string. + /// + /// Even if \c #kXMPFiles_ReturnsRawPacket is set, the returned packet information might have an + /// offset of -1 to indicate an unknown offset. While all file handlers should be able to return + /// the raw packet, some might not know the offset of the packet within the file. This is + /// typical in cases where external libraries are used. These cases might not even allow return + /// of the raw packet. + /// + /// @return True if the format has explicit "smart" support, false if the format is handled by + /// the default packet scanning plus heuristics. */ + + + static bool GetFormatInfo ( XMP_FileFormat format, + XMP_OptionBits * handlerFlags = 0 ); + + /// @} + + // ============================================================================================= + /// \name File operations + /// @{ + /// + /// These functions allow you to open, close, and query files. + + // --------------------------------------------------------------------------------------------- + /// @brief \c CheckFileFormat() tries to determine the format of a file. + /// + /// Tries to determine the format of a file, returning an \c #XMP_FileFormat value. Uses the + /// same logic as \c OpenFile() to select a smart handler. + /// + /// @param filePath The path for the file, appropriate for the local operating system. Passed as + /// a nul-terminated UTF-8 string. The path is the same as would be passed to \c OpenFile. + /// + /// @return The file's format if a smart handler would be selected by \c OpenFile(), otherwise + /// \c #kXMP_UnknownFile. + + static XMP_FileFormat CheckFileFormat ( XMP_StringPtr filePath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c CheckPackageFormat() tries to determine the format of a "package" folder. + /// + /// Tries to determine the format of a package, given the name of the top-level folder. Returns + /// an \c #XMP_FileFormat value. Examples of recognized packages include the video formats P2, + /// XDCAM, or Sony HDV. These packages contain collections of "clips", stored as multiple files + /// in specific subfolders. + /// + /// @param folderPath The path for the top-level folder, appropriate for the local operating + /// system. Passed as a nul-terminated UTF-8 string. This is not the same path you would pass to + /// \c OpenFile(). For example, the top-level path for a package might be ".../MyMovie", while + /// the path to a file you wish to open would be ".../MyMovie/SomeClip". + /// + /// @return The package's format if it can be determined, otherwise \c #kXMP_UnknownFile. + + static XMP_FileFormat CheckPackageFormat ( XMP_StringPtr folderPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetFileModDate() returns the last modification date of all files that are returned + /// by \c GetAssociatedResources() + /// + /// Returns the most recent O/S file modification date of all associated files. In the typical case + /// of a single file containing embedded XMP, returned date value is the modification date of the + /// same file. For sidecar and folder based video packages, returned date value is the modification + /// date of that associated file which was updated last. + /// + /// @param filePath A path exactly as would be passed to \c OpenFile. + /// + /// @param modDate A required pointer to return the last modification date. + /// + /// @param format A format hint as would be passed to \c OpenFile. + /// + /// @param options An optional set of option flags. The only defined one is \c kXMPFiles_ForceGivenHandler, + /// used to shortcut the handler selection logic if the caller is certain of the format. + /// + /// @return Returns true if the file path is valid to select a smart handler, false for an + /// invalid path or if fallback packet scanning would be selected. + + static bool GetFileModDate ( XMP_StringPtr filePath, + XMP_DateTime * modDate, + XMP_FileFormat * format = 0, + XMP_OptionBits options = 0 ); + + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetAssociatedResources() returns a list of files and folders associated to filePath. + /// + /// \c GetAssociatedResources is provided to locate all files that are associated to the given + /// filePath such as sidecar-based XMP or folder-based video packages.If a smart + /// handler can be selected (not fallback packet scanning) then a list of file/folder paths is + /// returned for the related files that can be safely copied/imported to a different location, + /// keeping intact metadata(XMP and non-XMP),content and the necessary folder structure of the + /// format. The necessary folder structure here is the structure that is needed to uniquely + /// identify a folder-based format.The filePath and format parameters are exactly as would be + /// used for OpenFile. In the simple embedded XMP case just one path is returned. In the simple + /// sidecar case one or two paths will be returned, one if there is no sidecar XMP and two if + /// sidecar XMP exists. For folder-based handlers paths to all associated files is returned, + /// including the files and folders necessary to identify the format.In general, all the returned + /// paths are existent.In case of folder based video formats the first associated resource in the + /// resourceList is the root folder. + /// + /// @param filePath A path exactly as would be passed to \c OpenFile. + /// + /// @param resourceList Address of a vector of strings to receive all associated resource paths. + /// + /// @param format A format hint as would be passed to \c OpenFile. + /// + /// @param options An optional set of option flags. The only defined one is \c kXMPFiles_ForceGivenHandler, + /// used to shortcut the handler selection logic if the caller is certain of the format. + /// + /// @return Returns true if the file path is valid to select a smart handler, false for an + /// invalid path or if fallback packet scanning would be selected. Can also return false for + /// unexpected errors that prevent knowledge of the file usage. + + static bool GetAssociatedResources ( XMP_StringPtr filePath, + std::vector* resourceList, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits options = 0); + + // --------------------------------------------------------------------------------------------- + /// @brief \c IsMetadataWritable() returns true if metadata can be updated for the given media path. + /// + /// \c IsMetadataWritable is provided to check if metadata can be updated or written to the format.In + /// the case of folder-based video formats only if all the metadata files can be written to, true is + /// returned.In other words, false is returned for a partial-write state of metadata files in + /// folder-based media formats. + /// + /// @param filePath A path exactly as would be passed to \c OpenFile. + /// + /// @param writable A pointer to the result flag. Is true if the metadata can be updated in the format, + /// otherwise false. + /// + /// @param format A format hint as would be passed to \c OpenFile. + /// + /// @param options An optional set of option flags. The only defined one is \c kXMPFiles_ForceGivenHandler, + /// used to shortcut the handler selection logic if the caller is certain of the format. + /// + /// @return Returns true if the file path is valid to select a smart handler, false for an + /// invalid path or if fallback packet scanning would be selected. + + static bool IsMetadataWritable (XMP_StringPtr filePath, + bool * writable, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c OpenFile() opens a file for metadata access. + /// + /// Opens a file for the requested forms of metadata access. Opening the file at a minimum + /// causes the raw XMP packet to be read from the file. If the file handler supports legacy + /// metadata reconciliation then legacy metadata is also read, unless \c #kXMPFiles_OpenOnlyXMP + /// is passed. + /// + /// If the file is opened for read-only access (passing \c #kXMPFiles_OpenForRead), the disk + /// file is closed immediately after reading the data from it; the \c XMPFiles object, however, + /// remains in the open state. You must call \c CloseFile() when finished using it. Other + /// methods, such as \c GetXMP(), can only be used between the \c OpenFile() and \c CloseFile() + /// calls. The \c XMPFiles destructor does not call \c CloseFile(); if you call it without + /// closing, any pending updates are lost. + /// + /// If the file is opened for update (passing \c #kXMPFiles_OpenForUpdate), the disk file + /// remains open until \c CloseFile() is called. The disk file is only updated once, when + /// \c CloseFile() is called, regardless of how many calls are made to \c PutXMP(). + /// + /// Typically, the XMP is not parsed and legacy reconciliation is not performed until \c GetXMP() + /// is called, but this is not guaranteed. Specific file handlers might do earlier parsing of + /// the XMP. Delayed parsing and early disk file close for read-only access are optimizations + /// to help clients implementing file browsers, so that they can access the file briefly + /// and possibly display a thumbnail, then postpone more expensive XMP processing until later. + /// + /// @param filePath The path for the file, appropriate for the local operating system. Passed as + /// a nul-terminated UTF-8 string. + /// + /// @param format The format of the file. If the format is unknown (\c #kXMP_UnknownFile) the + /// format is determined from the file content. The first handler to check is guessed from the + /// file's extension. Passing a specific format value is generally just a hint about what file + /// handler to try first (instead of the one based on the extension). If + /// \c #kXMPFiles_OpenStrictly is set, then any format other than \c #kXMP_UnknownFile requires + /// that the file actually be that format; otherwise an exception is thrown. + /// + /// @param openFlags A set of option flags that describe the desired access. By default (zero) + /// the file is opened for read-only access and the format handler decides on the level of + /// reconciliation that will be performed. A logical OR of these bit-flag constants: + /// + /// \li \c #kXMPFiles_OpenForRead - Open for read-only access. + /// \li \c #kXMPFiles_OpenForUpdate - Open for reading and writing. + /// \li \c #kXMPFiles_OpenOnlyXMP - Only the XMP is wanted, no reconciliation. + /// \li \c #kXMPFiles_OpenStrictly - Be strict about locating XMP and reconciling with other + /// forms. By default, a best effort is made to locate the correct XMP and to reconcile XMP + /// with other forms (if reconciliation is done). This option forces stricter rules, resulting + /// in exceptions for errors. The definition of strictness is specific to each handler, there + /// might be no difference. + /// \li \c #kXMPFiles_OpenUseSmartHandler - Require the use of a smart handler. + /// \li \c #kXMPFiles_OpenUsePacketScanning - Force packet scanning, do not use a smart handler. + /// \li \c #kXMPFiles_OptimizeFileLayout - When updating a file, spend the effort necessary + /// to optimize file layout. + /// + /// @return True if the file is succesfully opened and attached to a file handler. False for + /// anticipated problems, such as passing \c #kXMPFiles_OpenUseSmartHandler but not having an + /// appropriate smart handler. Throws an exception for serious problems. + + bool OpenFile ( XMP_StringPtr filePath, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits openFlags = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c OpenFile() opens a file for metadata access, using a string object + /// + /// Overloads the basic form of the function, allowing you to pass a string object for the file + /// path. It is otherwise identical; see details in the canonical form. + + bool OpenFile ( const tStringObj & filePath, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits openFlags = 0 ); + + #if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds. + // --------------------------------------------------------------------------------------------- + /// @brief \c OpenFile() opens a client-provided XMP_IO object for metadata access. + /// + /// Alternative to the basic form of the function, allowing you to pass an XMP_IO object for + /// client-managed I/O. + /// + + bool OpenFile ( XMP_IO * clientIO, + XMP_FileFormat format = kXMP_UnknownFile, + XMP_OptionBits openFlags = 0 ); + #endif + + // --------------------------------------------------------------------------------------------- + /// @brief CloseFile() explicitly closes an opened file. + /// + /// Performs any necessary output to the file and closes it. Files that are opened for update + /// are written to only when closing. + /// + /// If the file is opened for read-only access (passing \c #kXMPFiles_OpenForRead), the disk + /// file is closed immediately after reading the data from it; the \c XMPFiles object, however, + /// remains in the open state. You must call \c CloseFile() when finished using it. Other + /// methods, such as \c GetXMP(), can only be used between the \c OpenFile() and \c CloseFile() + /// calls. The \c XMPFiles destructor does not call \c CloseFile(); if you call it without closing, + /// any pending updates are lost. + /// + /// If the file is opened for update (passing \c #kXMPFiles_OpenForUpdate), the disk file remains + /// open until \c CloseFile() is called. The disk file is only updated once, when \c CloseFile() + /// is called, regardless of how many calls are made to \c PutXMP(). + /// + /// @param closeFlags Option flags for optional closing actions. This bit-flag constant is + /// defined: + /// + /// \li \c #kXMPFiles_UpdateSafely - Write into a temporary file then swap for crash safety. + + void CloseFile ( XMP_OptionBits closeFlags = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetFileInfo() retrieves basic information about an opened file. + /// + /// @param filePath [out] A buffer in which to return the path passed to \c OpenFile(). Can be + /// null if value is not wanted. + /// + /// @param openFlags [out] A buffer in which to return the option flags passed to + /// \c OpenFile(). Can be null if value is not wanted. + /// + /// @param format [out] A buffer in which to return the file format. Can be null if value is not + /// wanted. + /// @param handlerFlags [out] A buffer in which to return the handler's capability flags. Can + /// be null if value is not wanted. + /// + /// @return True if the file object is in the open state; that is, \c OpenFile() has been called + /// but \c CloseFile() has not. False otherwise. Even if the file object is open, the actual + /// disk file might be closed in the host file-system sense; see \c OpenFile(). + + bool GetFileInfo ( tStringObj * filePath = 0, + XMP_OptionBits * openFlags = 0, + XMP_FileFormat * format = 0, + XMP_OptionBits * handlerFlags = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetAbortProc() registers a callback function used to check for a user-signaled abort. + /// + /// The specified procedure is called periodically to allow a user to cancel time-consuming + /// operations. The callback function should return true to signal an abort, which results in an + /// exception being thrown. + /// + /// @param abortProc The callback function. + /// + /// @param abortArg A pointer to caller-defined data to pass to the callback function. + + void SetAbortProc ( XMP_AbortProc abortProc, + void * abortArg ); + + /// @} + + // ============================================================================================= + /// \name Accessing metadata + /// @{ + /// + /// These functions allow you to retrieve XMP metadata from open files, so that you can use the + /// \c TXMPMeta API to manipulate it. The \c PutXMP() functions update the XMP packet in memory. + /// Changed XMP is not actually written out to the file until the file is closed. + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetXMP() retrieves the XMP metadata from an open file. + /// + /// The function reports whether XMP is present in the file; you can choose to retrieve any or + /// all of the parsed XMP, the raw XMP packet,or information about the raw XMP packet. The + /// options provided when the file was opened determine if reconciliation is done with other + /// forms of metadata. + /// + /// @param xmpObj [out] An XMP object in which to return the parsed XMP metadata. Can be null. + /// + /// @param xmpPacket [out] An string object in which to return the raw XMP packet as stored in + /// the file. Can be null. The encoding of the packet is given in the \c packetInfo. Returns an + /// empty string if the low level file handler does not provide the raw packet. + /// + /// @param packetInfo [out] An string object in which to return the location and form of the raw + /// XMP in the file. \c #XMP_PacketInfo::charForm and \c #XMP_PacketInfo::writeable reflect the + /// raw XMP in the file. The parsed XMP property values are always UTF-8. The writeable flag is + /// taken from the packet trailer; it applies only to "format ignorant" writing. The + /// \c #XMP_PacketInfo structure always reflects the state of the XMP in the file. The offset, + /// length, and character form do not change as a result of calling \c PutXMP() unless the file + /// is also written. Some file handlers might not return location or contents of the raw packet + /// string. To determine whether one does, check the \c #kXMPFiles_ReturnsRawPacket bit returned + /// by \c GetFormatInfo(). If the low-level file handler does not provide the raw packet + /// location, \c #XMP_PacketInfo::offset and \c #XMP_PacketInfo::length are both 0, + /// \c #XMP_PacketInfo::charForm is UTF-8, and \c #XMP_PacketInfo::writeable is false. + /// + /// @return True if the file has XMP, false otherwise. + + bool GetXMP ( SXMPMeta * xmpObj = 0, + tStringObj * xmpPacket = 0, + XMP_PacketInfo * packetInfo = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c PutXMP() updates the XMP metadata in this object without writing out the file. + /// + /// This function supplies new XMP for the file. However, the disk file is not written until the + /// object is closed with \c CloseFile(). The options provided when the file was opened + /// determine if reconciliation is done with other forms of metadata. + /// + /// @param xmpObj The new metadata as an XMP object. + + void PutXMP ( const SXMPMeta & xmpObj ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c PutXMP() updates the XMP metadata in this object without writing out the file, + /// using a string object for input. + /// + /// Overloads the basic form of the function, allowing you to pass the metadata as a string object + /// instead of an XMP object. It is otherwise identical; see details in the canonical form. + /// + /// @param xmpPacket The new metadata as a string object containing a complete XMP packet. + + void PutXMP ( const tStringObj & xmpPacket ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c PutXMP() updates the XMP metadata in this object without writing out the file, + /// using a string object and optional length. + /// + /// Overloads the basic form of the function, allowing you to pass the metadata as a string object + /// instead of an XMP object. It is otherwise identical; see details in the canonical form. + /// + /// @param xmpPacket The new metadata as a const char * string containing an XMP packet. + /// + /// @param xmpLength Optional. The number of bytes in the string. If not supplied, the string is + /// assumed to be nul-terminated. + + void PutXMP ( XMP_StringPtr xmpPacket, + XMP_StringLen xmpLength = kXMP_UseNullTermination ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c CanPutXMP() reports whether this file can be updated with a specific XMP packet. + /// + /// Use to determine if the file can probably be updated with a given set of XMP metadata. This + /// depends on the size of the packet, the options with which the file was opened, and the + /// capabilities of the handler for the file format. The function obtains the length of the + /// serialized packet for the provided XMP, but does not keep it or modify it, and does not + /// cause the file to be written when closed. This is implemented roughly as follows: + /// + ///
+    /// bool CanPutXMP ( XMP_StringPtr xmpPacket )
+    /// {
+    ///    XMP_FileFormat format;
+    ///    this->GetFileInfo ( 0, &format, 0 );
+    ///
+    ///    XMP_OptionBits formatFlags;
+    ///    GetFormatInfo ( format, &formatFlags );
+    ///
+    ///    if ( (formatFlags & kXMPFiles_CanInjectXMP) && (formatFlags & kXMPFiles_CanExpand) ) return true;
+    ///
+    ///    XMP_PacketInfo packetInfo;
+    ///    bool hasXMP = this->GetXMP ( 0, 0, &packetInfo );
+    ///
+    ///    if ( ! hasXMP ) {
+    ///       if ( formatFlags & kXMPFiles_CanInjectXMP ) return true;
+    ///    } else {
+    ///       if ( (formatFlags & kXMPFiles_CanExpand) ||
+    ///            (packetInfo.length >= strlen(xmpPacket)) ) return true;
+    ///    }
+    ///    return false;
+    /// }
+    /// 
+ /// + /// @param xmpObj The proposed new metadata as an XMP object. + + bool CanPutXMP ( const SXMPMeta & xmpObj ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c CanPutXMP() reports whether this file can be updated with a specific XMP packet, + /// passed in a string object. + /// + /// Overloads the basic form of the function, allowing you to pass the metadata as a string object + /// instead of an XMP object. It is otherwise identical; see details in the canonical form. + /// + /// @param xmpPacket The proposed new metadata as a string object containing an XMP packet. + + bool CanPutXMP ( const tStringObj & xmpPacket ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c CanPutXMP() reports whether this file can be updated with a specific XMP packet, + /// passed in a string object. + /// + /// Overloads the basic form of the function, allowing you to pass the metadata as a string object + /// instead of an XMP object. It is otherwise identical; see details in the canonical form. + /// + /// @param xmpPacket The proposed new metadata as a const char * string containing an XMP packet. + /// + /// @param xmpLength Optional. The number of bytes in the string. If not supplied, the string + /// is assumed to be nul-terminated. + + bool CanPutXMP ( XMP_StringPtr xmpPacket, + XMP_StringLen xmpLength = kXMP_UseNullTermination ); + + /// @} + + // ============================================================================================= + /// \name Progress notifications + /// @{ + /// + /// These functions allow track the progress of file operations. Initially only file updates are + /// tracked, these all occur within calls to SXMPFiles::CloseFile. There are no plans to track + /// other operations at this time. Tracking support must be added to specific file handlers, + /// there are no guarantees about which handlers will have support. To simplify the logic only + /// file writes will be estimated and measured. + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetDefaultProgressCallback() sets a global default for progress tracking. This is + /// used as a default for XMPFiles (library) objects created after the default is set. This does + /// not affect the callback for new SXMPFiles (client) objects with an existing XMPFiles object. + /// + /// @param proc The client's callback function. Can be zero to disable notifications. + /// + /// @param context A pointer used to carry client-private context. + /// + /// @param interval The desired number of seconds between notifications. Ideally the first + /// notification is sent after this interval, then at each following multiple of this interval. + /// + /// @param sendStartStop A Boolean value indicating if initial and final notifications are + /// wanted in addition to those at the reporting intervals. + + static void SetDefaultProgressCallback ( XMP_ProgressReportProc proc, void * context = 0, + float interval = 1.0, bool sendStartStop = false ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProgressCallback() sets the progress notification callback for the associated + /// XMPFiles (library) object. + /// + /// @param proc The client's callback function. Can be zero to disable notifications. + /// + /// @param context A pointer used to carry client-private context. + /// + /// @param interval The desired number of seconds between notifications. Ideally the first + /// notification is sent after this interval, then at each following multiple of this interval. + /// + /// @param sendStartStop A Boolean value indicating if initial and final notifications are + /// wanted in addition to those at the reporting intervals. + + void SetProgressCallback ( XMP_ProgressReportProc proc, void * context = 0, + float interval = 1.0, bool sendStartStop = false ); + + /// @} + + // ============================================================================================= + // Error notifications + // =================== + + // --------------------------------------------------------------------------------------------- + /// \name Error notifications + /// @{ + /// + /// From the beginning through version 5.5, XMP Toolkit errors result in throwing an \c XMP_Error + /// exception. For the most part exceptions were thrown early and thus API calls aborted as soon + /// as an error was detected. Starting in version 5.5, support has been added for notifications + /// of errors arising in calls to \c TXMPFiles functions. + /// + /// A client can register an error notification callback function for a \c TXMPFile object. This + /// can be done as a global default or individually to each object. The global default applies + /// to all objects created after it is registered. Within the object there is no difference + /// between the global default or explicitly registered callback. The callback function returns + /// a \c bool value indicating if recovery should be attempted (true) or an exception thrown + /// (false). If no callback is registered, a best effort at recovery and continuation will be + /// made with an exception thrown if recovery is not possible. + /// + /// The number of notifications delivered for a given TXMPFiles object can be limited. This is + /// intended to reduce chatter from multiple or cascading errors. The limit is set when the + /// callback function is registered. This limits the number of notifications of the highest + /// severity delivered or less. If a higher severity error occurs, the counting starts again. + /// The limit and counting can be reset at any time, see \c ResetErrorCallbackLimit. + + // -------------------------------------------------------------------------------------------- + /// @brief SetDefaultErrorCallback() registers a global default error notification callback. + /// + /// @param proc The client's callback function. + /// + /// @param context Client-provided context for the callback. + /// + /// @param limit A limit on the number of notifications to be delivered. + + static void SetDefaultErrorCallback ( XMPFiles_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 ); + + // -------------------------------------------------------------------------------------------- + /// @brief SetErrorCallback() registers an error notification callback. + /// + /// @param proc The client's callback function. + /// + /// @param context Client-provided context for the callback. + /// + /// @param limit A limit on the number of notifications to be delivered. + + void SetErrorCallback ( XMPFiles_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 ); + + // -------------------------------------------------------------------------------------------- + /// @brief ResetErrorCallbackLimit() resets the error notification limit and counting. It has no + /// effect if an error notification callback function is not registered. + /// + /// @param limit A limit on the number of notifications to be delivered. + + void ResetErrorCallbackLimit ( XMP_Uns32 limit = 1 ); + + /// @} + + // ============================================================================================= + +private: + + XMPFilesRef xmpFilesRef; + + // These are used as callbacks from the library code to the client when returning values that + // involve heap allocations. This ensures the allocations occur within the client. + static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ); + static void SetClientStringVector ( void * clientPtr, XMP_StringPtr* arrayPtr, XMP_Uns32 stringCount ); + +}; // class TXMPFiles + +// ================================================================================================= + +#endif // __TXMPFiles_hpp__ diff --git a/source/lib/xmp_core/public/include/TXMPIterator.hpp b/source/lib/xmp_core/public/include/TXMPIterator.hpp new file mode 100644 index 0000000..603db68 --- /dev/null +++ b/source/lib/xmp_core/public/include/TXMPIterator.hpp @@ -0,0 +1,235 @@ +#ifndef __TXMPIterator_hpp__ +#define __TXMPIterator_hpp__ 1 + +#if ( ! __XMP_hpp__ ) + #error "Do not directly include, use XMP.hpp" +#endif + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================= +/// \file TXMPIterator.hpp +/// \brief API for access to the XMP Toolkit iteration services. +/// +/// \c TXMPIterator is the template class providing iteration services for the XMP Toolkit. It must +/// be instantiated with a string class such as \c std::string. See the instructions in XMP.hpp, and +/// the Overview for a discussion of the overall architecture of the XMP API. +// ================================================================================================= + +// ================================================================================================= +/// \class TXMPIterator TXMPIterator.hpp +/// @brief API for access to the XMP Toolkit iteration services. +/// +/// \c TXMPIterator provides a uniform means to iterate over the schema and properties within an XMP +/// object. \c TXMPIterator is a template class which must be instantiated with a string class such +/// as \c std::string. See the instructions in XMP.hpp, and the Overview for a discussion of the +/// overall architecture of the XMP API. Access these functions through the concrete class, +/// \c SXMPIterator. +/// +/// @note Only XMP object iteration is currently available. Future development may include iteration +/// over global tables, such as registered namespaces. +/// +/// To understand how iteration works, you should have a thorough understanding of the XMP data +/// tree, as described in the XMP Specification Part 1. You might also find it helpful to create +/// some complex XMP and examine the output of \c TXMPMeta::DumpObject(). +/// +/// \li The top of the XMP data tree is a single root node. This does not explicitly appear in the +/// dump and is never visited by an iterator; that is, it is never returned from +/// \c TXMPIterator::Next(). +/// +/// \li Beneath the root are schema nodes; these collect the top-level properties in the same +/// namespace. They are created and destroyed implicitly. +/// +/// \li Beneath the schema nodes are the property nodes. The nodes below a property node depend on +/// its type (simple, struct, or array) and whether it has qualifiers. +/// +/// A \c TXMPIterator constructor defines a starting point for the iteration, and options that +/// control how it proceeds. By default, iteration starts at the root and visits all nodes beneath +/// it in a depth-first manner. The root node iteself is not visited; the first visited node is a +/// schema node. You can provide a schema name or property path to select a different starting node. +/// By default, this visits the named root node first then all nodes beneath it in a depth-first +/// manner. +/// +/// The function \c TXMPIterator::Next() delivers the schema URI, path, and option flags for the +/// node being visited. If the node is simple, it also delivers the value. Qualifiers for this node +/// are visited next. The fields of a struct or items of an array are visited after the qualifiers +/// of the parent. +/// +/// You can specify options when contructing the iteration object to control how the iteration is +/// performed. +/// +/// \li \c #kXMP_IterJustChildren - Visit just the immediate children of the root. Skip the root +/// itself and all nodes below the immediate children. This omits the qualifiers of the immediate +/// children, the qualifier nodes being below what they qualify. +/// \li \c #kXMP_IterJustLeafNodes - Visit just the leaf property nodes and their qualifiers. +/// \li \c #kXMP_IterJustLeafName - Return just the leaf component of the node names. The default +/// is to return the full path name. +/// \li \c #kXMP_IterOmitQualifiers - Do not visit the qualifiers of a node. +// ================================================================================================= + +#include "client-glue/WXMPIterator.hpp" + +template class TXMPIterator { + +public: + + // --------------------------------------------------------------------------------------------- + /// @brief Assignment operator, assigns the internal ref and increments the ref count. + /// + /// Assigns the internal reference from an existing object and increments the reference count on + /// the underlying internal XMP iterator. + /// + /// @param rhs An existing iteration object. + + void operator= ( const TXMPIterator & rhs ); + + // --------------------------------------------------------------------------------------------- + /// @brief Copy constructor, creates a client object refering to the same internal object. + /// + /// Creates a new client iterator that refers to the same underlying iterator as an existing object. + /// + /// @param original An existing iteration object to copy. + + TXMPIterator ( const TXMPIterator & original ); + + // --------------------------------------------------------------------------------------------- + /// @brief Constructs an iterator for properties within a schema in an XMP object. + /// + /// See the class description for the general operation of an XMP object iterator. + /// Overloaded forms are provided to iterate the entire data tree, + /// a subtree rooted at a specific node, or properties within a specific schema. + /// + /// @param xmpObj The XMP object over which to iterate. + /// + /// @param schemaNS Optional schema namespace URI to restrict the iteration. To visit all of the + /// schema, pass 0 or the empty string "". + /// + /// @param propName Optional property name to restrict the iteration. May be an arbitrary path + /// expression. If provided, a schema URI must also be provided. To visit all properties, pass 0 + /// or the empty string "". + /// + /// @param options Option flags to control the iteration. A logical OR of these bit flag constants: + /// \li \c #kXMP_IterJustChildren - Visit only the immediate children of the root; default visits subtrees. + /// \li \c #kXMP_IterJustLeafNodes - Visit only the leaf nodes; default visits all nodes. + /// \li \c #kXMP_IterJustLeafName - Return just the leaf part of the path; default returns the full path. + /// \li \c #kXMP_IterOmitQualifiers - Omit all qualifiers. + /// + /// @return The new TXMPIterator object. + + TXMPIterator ( const TXMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief Constructs an iterator for a subtree of properties within an XMP object. + /// + /// See the class description for the general operation of an XMP object iterator. Overloaded + /// forms are provided to iterate the entire data tree, a subtree rooted at a specific node, or + /// properties within a specific schema. + /// + /// @param xmpObj The XMP object over which to iterate. + /// + /// @param schemaNS Optional schema namespace URI to restrict the iteration. To visit all of the + /// schema, pass 0 or the empty string "". + /// + /// @param options Option flags to control the iteration. A logical OR of these bit flag constants: + /// \li \c #kXMP_IterJustChildren - Visit only the immediate children of the root; default visits subtrees. + /// \li \c #kXMP_IterJustLeafNodes - Visit only the leaf nodes; default visits all nodes. + /// \li \c #kXMP_IterJustLeafName - Return just the leaf part of the path; default returns the full path. + /// \li \c #kXMP_IterOmitQualifiers - Omit all qualifiers. + /// + /// @return The new TXMPIterator object. + + TXMPIterator ( const TXMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief Constructs an iterator for the entire data tree within an XMP object. + /// + /// See the class description for the general operation of an XMP object iterator. Overloaded + /// forms are provided to iterate the entire data tree, a subtree rooted at a specific node, or + /// properties within a specific schema. + /// + /// @param xmpObj The XMP object over which to iterate. + /// + /// @param options Option flags to control the iteration. A logical OR of these bit flag constants: + /// \li \c #kXMP_IterJustChildren - Visit only the immediate children of the root; default visits subtrees. + /// \li \c #kXMP_IterJustLeafNodes - Visit only the leaf nodes; default visits all nodes. + /// \li \c #kXMP_IterJustLeafName - Return just the leaf part of the path; default returns the full path. + /// \li \c #kXMP_IterOmitQualifiers - Omit all qualifiers. + /// + /// @return The new \c TXMPIterator object. + + TXMPIterator ( const TXMPMeta & xmpObj, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief Constructs an iterator for the global tables of the XMP toolkit. Not implemented. + + TXMPIterator ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options ); + + // --------------------------------------------------------------------------------------------- + /// @brief Destructor, typical virtual destructor. + + virtual ~TXMPIterator() throw(); + + // --------------------------------------------------------------------------------------------- + /// @brief \c Next() visits the next node in the iteration. + /// + /// Proceeds to the next node according to the options specified on creation of this object, and + /// delivers the schema URI, path, and option flags for the node being visited. If the node is + /// simple, it also delivers the value. + /// + /// @param schemaNS [out] A string object in which to return the assigned the schema namespace + /// URI of the current property. Can be null if the value is not wanted. + /// + /// @param propPath [out] A string object in which to return the XPath name of the current + /// property. Can be null if the value is not wanted. + /// + /// @param propValue [out] A string object in which to return the value of the current + /// property. Can be null if the value is not wanted. + /// + /// @param options [out] A buffer in which to return the flags describing the current property, + /// which are a logical OR of \c #XMP_OptionBits bit-flag constants. + /// + /// @return True if there was another node to visit, false if the iteration is complete. + + bool Next ( tStringObj * schemaNS = 0, + tStringObj * propPath = 0, + tStringObj * propValue = 0, + XMP_OptionBits * options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c Skip() skips some portion of the remaining iterations. + /// + /// @param options Option flags to control the iteration, a logical OR of these bit-flag + /// constants: + /// \li \c #kXMP_IterSkipSubtree - Skip the subtree below the current node. + /// \li \c #kXMP_IterSkipSiblings - Skip the subtree below and remaining siblings of the current node. + + void Skip ( XMP_OptionBits options ); + +private: + + XMPIteratorRef iterRef; + + TXMPIterator(); // ! Hidden, must choose property or table iteration. + + static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ); + +}; // class TXMPIterator + +// ================================================================================================= + +#endif // __TXMPIterator_hpp__ diff --git a/source/lib/xmp_core/public/include/TXMPMeta.hpp b/source/lib/xmp_core/public/include/TXMPMeta.hpp new file mode 100644 index 0000000..57aa62a --- /dev/null +++ b/source/lib/xmp_core/public/include/TXMPMeta.hpp @@ -0,0 +1,1751 @@ +#ifndef __TXMPMeta_hpp__ +#define __TXMPMeta_hpp__ 1 + +#if ( ! __XMP_hpp__ ) + #error "Do not directly include, use XMP.hpp" +#endif + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================= +/// \file TXMPMeta.hpp +/// \brief API for access to the XMP Toolkit core services. +/// +/// \c TXMPMeta is the template class providing the core services of the XMP Toolkit. It must be +/// instantiated with a string class such as \c std::string. Read the Toolkit Overview for +/// information about the overall architecture of the XMP API, and the documentation for \c XMP.hpp +/// for specific instantiation instructions. Please that you MUST NOT derive a class from this class, +/// consider this class FINAL, use it directly. [1279031] +/// +/// Access these functions through the concrete class, \c SXMPMeta. +// ================================================================================================= + +// ================================================================================================= +/// \class TXMPMeta TXMPMeta.hpp +/// \brief API for access to the XMP Toolkit core services. +/// +/// \c TXMPMeta is the template class providing the core services of the XMP Toolkit. It should be +/// instantiated with a string class such as \c std::string. Read the Toolkit Overview for +/// information about the overall architecture of the XMP API, and the documentation for \c XMP.hpp +/// for specific instantiation instructions. +/// +/// Access these functions through the concrete class, \c SXMPMeta. +/// +/// You can create \c TXMPMeta objects (also called XMP objects) from metadata that you construct, +/// or that you obtain from files using the XMP Toolkit's XMPFiles component; see \c TXMPFiles.hpp. +// ================================================================================================= + +template class TXMPIterator; +template class TXMPUtils; + +// ------------------------------------------------------------------------------------------------- + +template class TXMPMeta { + +public: + + // ============================================================================================= + // Initialization and termination + // ============================== + + // --------------------------------------------------------------------------------------------- + /// \name Initialization and termination + /// + /// @{ + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetVersionInfo() retrieves runtime version information. + /// + /// The header \c XMPVersion.hpp defines a static version number for the XMP Toolkit, which + /// describes the version of the API used at client compile time. It is not necessarily the same + /// as the runtime version. Do not base runtime decisions on the static version alone; you can, + /// however, compare the runtime and static versions. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). The + /// function can be called before calling \c TXMPMeta::Initialize(). + /// + /// @param info [out] A buffer in which to return the version information. + + static void GetVersionInfo ( XMP_VersionInfo * info ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c Initialize() explicitly initializes the XMP Toolkit before use. */ + + /// Initializes the XMP Toolkit. + /// + /// Call this function before making any other calls to the \c TXMPMeta functions, except + /// \c TXMPMeta::GetVersionInfo(). + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + /// + /// @return True on success. */ + static bool Initialize(); + // --------------------------------------------------------------------------------------------- + /// @brief \c Terminate() explicitly terminates usage of the XMP Toolkit. + /// + /// Frees structures created on initialization. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + + static void Terminate(); + + /// @} + + // ============================================================================================= + // Constuctors and destructor + // ========================== + + // --------------------------------------------------------------------------------------------- + /// \name Constructors and destructor + /// @{ + + // --------------------------------------------------------------------------------------------- + /// @brief Default constructor, creates an empty object. + /// + /// The default constructor creates a new empty \c TXMPMeta object. + /// + /// @return The new object. */ + TXMPMeta(); + + // --------------------------------------------------------------------------------------------- + /// @brief Copy constructor, creates a client object refering to the same internal object. + /// + /// The copy constructor creates a new \c TXMPMeta object that refers to the same internal XMP + /// object. as an existing \c TXMPMeta object. + /// + /// @param original The object to copy. + /// + /// @return The new object. */ + + TXMPMeta ( const TXMPMeta & original ); + + // --------------------------------------------------------------------------------------------- + /// @brief Assignment operator, assigns the internal reference and increments the reference count. + /// + /// The assignment operator assigns the internal ref from the rhs object and increments the + /// reference count on the underlying internal XMP object. + + void operator= ( const TXMPMeta & rhs ); + + // --------------------------------------------------------------------------------------------- + /// @brief Reconstructs an XMP object from an internal reference. + /// + /// This constructor creates a new \c TXMPMeta object that refers to the underlying reference object + /// of an existing \c TXMPMeta object. Use to safely pass XMP objects across DLL boundaries. + /// + /// @param xmpRef The underlying reference object, obtained from some other XMP object with + /// \c TXMPMeta::GetInternalRef(). + /// + /// @return The new object. + + TXMPMeta ( XMPMetaRef xmpRef ); + + // --------------------------------------------------------------------------------------------- + /// @brief Constructs an object and parse one buffer of RDF into it. + /// + /// This constructor creates a new \c TXMPMeta object and populates it with metadata from a + /// buffer containing serialized RDF. This buffer must be a complete RDF parse stream. + /// + /// The result of passing serialized data to this function is identical to creating an empty + /// object then calling \c TXMPMeta::ParseFromBuffer(). To use the constructor, however, the RDF + /// must be complete. If you need to parse data from multiple buffers, create an empty object + /// and use \c TXMPMeta::ParseFromBuffer(). + /// + /// @param buffer A pointer to the buffer of RDF to be parsed. Can be null if the length is 0; + /// in this case, the function creates an empty object. + /// + /// @param xmpSize The length in bytes of the buffer. + /// + /// @return The new object. + + TXMPMeta ( XMP_StringPtr buffer, + XMP_StringLen xmpSize ); + + // --------------------------------------------------------------------------------------------- + /// @brief Destructor, typical virtual destructor. */ + virtual ~TXMPMeta() throw(); + + /// @} + + // ============================================================================================= + // Global state functions + // ====================== + + // --------------------------------------------------------------------------------------------- + /// \name Global option flags + /// @{ + /// Global option flags affect the overall behavior of the XMP Toolkit. The available options + /// will be declared in \c XMP_Const.h. There are none in this version of the Toolkit. + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetGlobalOptions() retrieves the set of global option flags. There are none in + /// this version of the Toolkit. + /// + /// This function is static; you can make the call from the class without instantiating it. + /// + /// @return A logical OR of global option bit-flag constants. + + static XMP_OptionBits GetGlobalOptions(); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetGlobalOptions() updates the set of global option flags. There are none in this + /// version of the Toolkit. + /// + /// The entire set is replaced with the new values. If only one flag is to be modified, use + /// \c TXMPMeta::GetGlobalOptions() to obtain the current set, modify the desired flag, then use + /// this function to reset the value. + /// + /// This function is static; you can make the call from the class without instantiating it. + /// + /// @param options A logical OR of global option bit-flag constants. + + static void SetGlobalOptions ( XMP_OptionBits options ); + + /// @} + + // --------------------------------------------------------------------------------------------- + /// \name Internal data structure dump utilities + /// @{ + /// + /// These are debugging utilities that dump internal data structures, to be handled by + /// client-defined callback described in \c XMP_Const.h. + /// + /// @see Member function \c TXMPMeta::DumpObject() + + // --------------------------------------------------------------------------------------------- + /// @brief \c DumpNamespaces() sends the list of registered namespace URIs and prefixes to a handler. + /// + /// For debugging. Invokes a client-defined callback for each line of output. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + /// + /// @param outProc The client-defined procedure to handle each line of output. + /// + /// @param clientData A pointer to client-defined data to pass to the handler. + /// + /// @return A success-fail status value, returned from the handler. Zero is success, failure + /// values are client-defined. + + static XMP_Status DumpNamespaces ( XMP_TextOutputProc outProc, + void * clientData ); + + /// @} + + // --------------------------------------------------------------------------------------------- + /// \name Namespace Functions + /// @{ + /// + /// Namespaces must be registered before use in namespace URI parameters or path expressions. + /// Within the XMP Toolkit the registered namespace URIs and prefixes must be unique. Additional + /// namespaces encountered when parsing RDF are automatically registered. + /// + /// The namespace URI should always end in an XML name separator such as '/' or '#'. This is + /// because some forms of RDF shorthand catenate a namespace URI with an element name to form a + /// new URI. + + // --------------------------------------------------------------------------------------------- + /// @brief \c RegisterNamespace() registers a namespace URI with a suggested prefix. + /// + /// If the URI is not registered but the suggested prefix is in use, a unique prefix is created + /// from the suggested one. The actual registered prefix is returned. The function result tells + /// if the registered prefix is the suggested one. It is not an error if the URI is already + /// registered, regardless of the prefix. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + /// + /// @param namespaceURI The URI for the namespace. Must be a valid XML URI. + /// + /// @param suggestedPrefix The suggested prefix to be used if the URI is not yet registered. + /// Must be a valid XML name. + /// + /// @param registeredPrefix [out] A string object in which to return the prefix actually + /// registered for this URI. + /// + /// @return True if the registered prefix matches the suggested prefix. + /// + /// @note No checking is done on either the URI or the prefix. */ + + static bool RegisterNamespace ( XMP_StringPtr namespaceURI, + XMP_StringPtr suggestedPrefix, + tStringObj * registeredPrefix ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetNamespacePrefix() obtains the prefix for a registered namespace URI, and + /// reports whether the URI is registered. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + /// + /// @param namespaceURI The URI for the namespace. Must not be null or the empty string. It is + /// not an error if the namespace URI is not registered. + /// + /// @param namespacePrefix [out] A string object in which to return the prefix registered for + /// this URI, with a terminating colon character, ':'. If the namespace is not registered, this + /// string is not modified. + /// + /// @return True if the namespace URI is registered. + + static bool GetNamespacePrefix ( XMP_StringPtr namespaceURI, + tStringObj * namespacePrefix ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetNamespaceURI() obtains the URI for a registered namespace prefix, and reports + /// whether the prefix is registered. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + /// + /// @param namespacePrefix The prefix for the namespace. Must not be null or the empty string. + /// It is not an error if the namespace prefix is not registered. + /// + /// @param namespaceURI [out] A string object in which to return the URI registered for this + /// prefix. If the prefix is not registered, this string is not modified. + /// + /// @return True if the namespace prefix is registered. + + static bool GetNamespaceURI ( XMP_StringPtr namespacePrefix, + tStringObj * namespaceURI ); + + // --------------------------------------------------------------------------------------------- + /// @brief Not implemented. + /// + /// Deletes a namespace from the registry. Does nothing if the URI is not registered, or if the + /// parameter is null or the empty string. + /// + /// This function is static; make the call directly from the concrete class (\c SXMPMeta). + /// + /// @param namespaceURI The URI for the namespace. + + static void DeleteNamespace ( XMP_StringPtr namespaceURI ); + + /// @} + + // ============================================================================================= + // Basic property manipulation functions + // ===================================== + + // *** Should add discussion of schemaNS and propName prefix usage. + + // --------------------------------------------------------------------------------------------- + /// \name Accessing property values + /// @{ + /// + /// The property value accessors all take a property specification; the top level namespace URI + /// (the "schema" namespace) and the basic name of the property being referenced. See the + /// introductory discussion of path expression usage for more information. + /// + /// The accessor functions return true if the specified property exists. If it does, output + /// parameters return the value (if any) and option flags describing the property. The option + /// bit-flag constants that describe properties are \c kXMP_PropXx and + /// \c kXMP_ArrayIsXx. See \c #kXMP_PropValueIsURI and following, and macros \c #XMP_PropIsSimple + /// and following in \c XMP_Const.h. If the property exists and has a value, it is returned as a + /// Unicode string in UTF-8 encoding. Arrays and the non-leaf levels of structs do not have + /// values. + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetProperty() reports whether a property exists, and retrieves its value. + /// + /// This is the simplest property accessor. Use this to retrieve the values of top-level simple + /// properties, or after using the path composition functions in \c TXMPUtils. + /// + /// When specifying a namespace and path (in this and all other accessors): + /// \li If a namespace URI is specified, it must be for a registered namespace. + /// \li If the namespace is specified only by a prefix in the property name path, + /// it must be a registered prefix. + /// \li If both a URI and path prefix are present, they must be corresponding + /// parts of a registered namespace. + /// + /// @param schemaNS The namespace URI for the property. The URI must be for a registered + /// namespace. Must not be null or the empty string. + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string. The first component can be a namespace prefix; if present without a + /// \c schemaNS value, the prefix specifies the namespace. The prefix must be for a registered + /// namespace, and if a namespace URI is specified, must match the registered prefix for that + /// namespace. + /// + /// @param propValue [out] A string object in which to return the value of the property, if the + /// property exists and has a value. Arrays and non-leaf levels of structs do not have values. + /// Can be null if the value is not wanted. + /// + /// @param options A buffer in which to return option flags describing the property. Can be null + /// if the flags are not wanted. + /// + /// @return True if the property exists. + + bool GetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + tStringObj * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetArrayItem() provides access to items within an array. + /// + /// Reports whether the item exists; if it does, and if it has a value, the function retrieves + /// the value. Items are accessed by an integer index, where the first item has index 1. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem + /// to specify the last existing array item. + /// + /// @param itemValue [out] A string object in which to return the value of the array item, if it + /// has a value. Arrays and non-leaf levels of structs do not have values. Can be null if the + /// value is not wanted. + /// + /// @param options [out] A buffer in which to return the option flags describing the array item. + /// Can be null if the flags are not wanted. + /// + /// @return True if the array item exists. + + bool GetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + tStringObj * itemValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetStructField() provides access to fields within a nested structure. + /// + /// Reports whether the field exists; if it does, and if it has a value, the function retrieves + /// the value. + /// + /// @param schemaNS The namespace URI for the struct; see \c GetProperty(). + /// + /// @param structName The name of the struct. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field. Same URI and prefix usage as the \c schemaNS + /// and \c structName parameters. + /// + /// @param fieldName The name of the field. Must be a single XML name, must not be null or the + /// empty string. Same URI and prefix usage as the \c schemaNS and \c structName parameters. + /// + /// @param fieldValue [out] A string object in which to return the value of the field, if the + /// field has a value. Arrays and non-leaf levels of structs do not have values. Can be null if + /// the value is not wanted. + /// + /// @param options [out] A buffer in which to return the option flags describing the field. Can + /// be null if the flags are not wanted. + /// + /// @return True if the field exists. + + bool GetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + tStringObj * fieldValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetQualifier() provides access to a qualifier attached to a property. + /// + /// @note In this version of the Toolkit, qualifiers are supported only for simple leaf properties. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property to which the qualifier is attached. Can be a + /// general path expression, must not be null or the empty string; see \c GetProperty() for + /// namespace prefix usage. + /// + /// @param qualNS The namespace URI for the qualifier. Same URI and prefix usage as the + /// \c schemaNS and \c propName parameters. + /// + /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or + /// the empty string. Same URI and prefix usage as the \c schemaNS and \c propName parameters. + /// + /// @param qualValue [out] A string object in which to return the value of the qualifier, if the + /// qualifier has a value. Arrays and non-leaf levels of structs do not have values. Can be null + /// if the value is not wanted. + /// + /// @param options [out] A buffer in which to return the option flags describing the qualifier. + /// Can be null if the flags are not wanted. + /// + /// @return True if the qualifier exists. + + bool GetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + tStringObj * qualValue, + XMP_OptionBits * options ) const; + + /// @} + + // ============================================================================================= + + // --------------------------------------------------------------------------------------------- + /// \name Creating properties and setting their values + /// @{ + /// + /// These functions all take a property specification; the top level namespace URI (the "schema" + /// namespace) and the basic name of the property being referenced. See the introductory + /// discussion of path expression usage for more information. + /// + /// All of the functions take a UTF-8 encoded Unicode string for the property value. Arrays and + /// non-leaf levels of structs do not have values. The value can be passed as an + /// \c #XMP_StringPtr (a pointer to a null-terminated string), or as a string object + /// (\c tStringObj). + + /// Each function takes an options flag that describes the property. You can use these functions + /// to create empty arrays and structs by setting appropriate option flags. When you assign a + /// value, all levels of a struct that are implicit in the assignment are created if necessary. + /// \c TXMPMeta::AppendArrayItem() implicitly creates the named array if necessary. + /// + /// The allowed option bit-flags include: + /// \li \c #kXMP_PropValueIsStruct - Can be used to create an empty struct. + /// A struct is implicitly created when the first field is set. + /// \li \c #kXMP_PropValueIsArray - By default, a general unordered array (bag). + /// \li \c #kXMP_PropArrayIsOrdered - An ordered array. + /// \li \c #kXMP_PropArrayIsAlternate - An alternative array. + /// \li \c #kXMP_PropArrayIsAltText - An alt-text array. Each array element must + /// be a simple property with an \c xml:lang attribute. + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty() creates or sets a property value. + /// + /// This is the simplest property setter. Use it for top-level simple properties, or after using + /// the path composition functions in \c TXMPUtils. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue The new value, a pointer to a null terminated UTF-8 string. Must be null + /// for arrays and non-leaf levels of structs that do not have values. + /// + /// @param options Option flags describing the property; a logical OR of allowed bit-flag + /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property + /// that already exists. + + void SetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr propValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty() creates or sets a property value using a string object. + /// + /// Overloads the basic form of the function, allowing you to pass a string object + /// for the item value. It is otherwise identical; see details in the canonical form. + + void SetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const tStringObj & propValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetArrayItem() creates or sets the value of an item within an array. + /// + /// Items are accessed by an integer index, where the first item has index 1. This function + /// creates the item if necessary, but the array itself must already exist Use + /// \c AppendArrayItem() to create arrays. A new item is automatically appended if the index is the + /// array size plus 1. To insert a new item before or after an existing item, use option flags. + /// + /// Use \c TXMPUtils::ComposeArrayItemPath() to create a complex path. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem + /// to specify the last existing array item. + /// + /// @param itemValue The new item value, a null-terminated UTF-8 string, if the array item has a + /// value. + /// + /// @param options Option flags describing the array type and insertion location for a new item; + /// a logical OR of allowed bit-flag constants. The type, if specified, must match the existing + /// array type, \c #kXMP_PropArrayIsOrdered, \c #kXMP_PropArrayIsAlternate, or + /// \c #kXMP_PropArrayIsAltText. Default (0 or \c #kXMP_NoOptions) matches the existing array type. + /// + /// To insert a new item before or after the specified index, set flag \c #kXMP_InsertBeforeItem + /// or \c #kXMP_InsertAfterItem. + + void SetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_StringPtr itemValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetArrayItem() creates or sets the value of an item within an array using a string object. + /// + /// Overloads the basic form of the function, allowing you to pass a string object in which to + /// return the item value. It is otherwise identical; see details in the canonical form. + + void SetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + const tStringObj & itemValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c AppendArrayItem() adds an item to an array, creating the array if necessary. + /// + /// This function simplifies construction of an array by not requiring that you pre-create an + /// empty array. The array that is assigned is created automatically if it does not yet exist. + /// If the array exists, it must have the form specified by the options. Each call appends a new + /// item to the array. + /// + /// Use \c TXMPUtils::ComposeArrayItemPath() to create a complex path. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param arrayOptions Option flags describing the array type to create; a logical OR of + /// allowed bit-flag constants, \c #kXMP_PropArrayIsOrdered, \c #kXMP_PropArrayIsAlternate, or + /// \c #kXMP_PropArrayIsAltText. If the array exists, must match the existing array type or be + /// null (0 or \c #kXMP_NoOptions). + /// + /// @param itemValue The new item value, a null-terminated UTF-8 string, if the array item has a + /// value. + /// + /// @param itemOptions Option flags describing the item type to create; one of the bit-flag + /// constants \c #kXMP_PropValueIsArray or \c #kXMP_PropValueIsStruct to create a complex array + /// item. + + void AppendArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + XMP_StringPtr itemValue, + XMP_OptionBits itemOptions = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c AppendArrayItem() adds an item to an array using a string object value, creating + /// the array if necessary. + /// + /// Overloads the basic form of the function, allowing you to pass a string object in which to + /// return the item value. It is otherwise identical; see details in the canonical form. + + void AppendArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + const tStringObj & itemValue, + XMP_OptionBits itemOptions = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetStructField() creates or sets the value of a field within a nested structure. + /// + /// Use this to set a value within an existing structure, create a new field within an existing + /// structure, or create an empty structure of any depth. If you set a field in a structure that + /// does not exist, the structure is automatically created. + /// + /// Use \c TXMPUtils::ComposeStructFieldPath() to create a complex path. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param structName The name of the struct. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field. Same namespace and prefix usage as + /// \c GetProperty(). + /// + /// @param fieldName The name of the field. Must be a single XML name, must not be null or the + /// empty string. Same namespace and prefix usage as \c GetProperty(). + /// + /// @param fieldValue The new value, a null-terminated UTF-8 string, if the field has a value. + /// Null to create a new, empty struct or empty field in an existing struct. + /// + /// @param options Option flags describing the property, in which the bit-flag + /// \c #kXMP_PropValueIsStruct must be set to create a struct. + + void SetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetStructField() creates or sets the value of a field within a nested structure, + /// using a string object. + /// + /// Overloads the basic form of the function, allowing you to pass a string object in which to + /// return the field value. It is otherwise identical; see details in the canonical form. + + void SetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + const tStringObj & fieldValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetQualifier() creates or sets a qualifier attached to a property. + /// + /// Use this to set a value for an existing qualifier, or create a new qualifier. <> Use + /// \c TXMPUtils::ComposeQualifierPath() to create a complex path. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property to which the qualifier is attached. Can be a + /// general path expression, must not be null or the empty string; see \c GetProperty() for + /// namespace prefix usage. + /// + /// @param qualNS The namespace URI for the qualifier. Same namespace and prefix usage as + /// \c GetProperty(). + /// + /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or + /// the empty string. Same namespace and prefix usage as \c GetProperty(). + /// + /// @param qualValue The new value, a null-terminated UTF-8 string, if the qualifier has a + /// value. Null to create a new, empty qualifier. + /// + /// @param options Option flags describing the <>, a logical OR + /// of property-type bit-flag constants. Use the macro \c #XMP_PropIsQualifier to create a + /// qualifier. <> + + void SetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_StringPtr qualValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetQualifier() creates or sets a qualifier attached to a property using a string object. + /// + /// Overloads the basic form of the function, allowing you to pass a string object + /// for the qualifier value. It is otherwise identical; see details in the canonical form. + + void SetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + const tStringObj & qualValue, + XMP_OptionBits options = 0 ); + + /// @} + + // ============================================================================================= + + // --------------------------------------------------------------------------------------------- + /// \name Detecting and deleting properties. + /// @{ + /// + /// The namespace URI and prefix usage for property specifiers in these functions is the same as + /// for \c TXMPMeta::GetProperty(). + + // --------------------------------------------------------------------------------------------- + /// @brief \c DeleteProperty() deletes an XMP subtree rooted at a given property. + /// + /// It is not an error if the property does not exist. + /// + /// @param schemaNS The namespace URI for the property; see \c GetProperty(). + /// + /// @param propName The name of the property; see \c GetProperty(). + + void DeleteProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DeleteArrayItem() deletes an XMP subtree rooted at a given array item. + /// + /// It is not an error if the array item does not exist. Use + /// \c TXMPUtils::ComposeArrayItemPath() to create a complex path. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem + /// to specify the last existing array item. + + void DeleteArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DeleteStructField() deletes an XMP subtree rooted at a given struct field. + /// + /// It is not an error if the field does not exist. + /// + /// @param schemaNS The namespace URI for the struct; see \c GetProperty(). + /// + /// @param structName The name of the struct. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field. Same namespace and prefix usage as + /// \c GetProperty(). + /// + /// @param fieldName The name of the field. Must be a single XML name, must not be null or the + /// empty string. Same namespace and prefix usage as \c GetProperty(). + + void DeleteStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DeleteQualifier() deletes an XMP subtree rooted at a given qualifier. + /// + /// It is not an error if the qualifier does not exist. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property to which the qualifier is attached. Can be a + /// general path expression, must not be null or the empty string; see \c GetProperty() for + /// namespace prefix usage. + /// + /// @param qualNS The namespace URI for the qualifier. Same namespace and prefix usage as + /// \c GetProperty(). + /// + /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or + /// the empty string. Same namespace and prefix usage as \c GetProperty(). + + void DeleteQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DoesPropertyExist() reports whether a property currently exists. + /// + /// @param schemaNS The namespace URI for the property; see \c GetProperty(). + /// + /// @param propName The name of the property; see \c GetProperty(). + /// + /// @return True if the property exists. + + bool DoesPropertyExist ( XMP_StringPtr schemaNS, + XMP_StringPtr propName ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c DoesArrayItemExist() reports whether an array item currently exists. + /// + /// Use \c TXMPUtils::ComposeArrayItemPath() to create a complex path. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param itemIndex The 1-based index of the desired item. Use the macro \c #kXMP_ArrayLastItem + /// to specify the last existing array item. + /// + /// @return True if the array item exists. + + bool DoesArrayItemExist ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c DoesStructFieldExist() reports whether a struct field currently exists. + /// + /// Use \c TXMPUtils::ComposeStructFieldPath() to create a complex path. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param structName The name of the struct. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field. Same namespace and prefix usage as + /// \c GetProperty(). + /// + /// @param fieldName The name of the field. Must be a single XML name, must not be null or the + /// empty string. Same namespace and prefix usage as \c GetProperty(). + /// + /// @return True if the field exists. + + bool DoesStructFieldExist ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c DoesQualifierExist() reports whether a qualifier currently exists. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property to which the qualifier is attached. Can be a + /// general path expression, must not be null or the empty string; see \c GetProperty() for + /// namespace prefix usage. + /// + /// @param qualNS The namespace URI for the qualifier. Same namespace and prefix usage as + /// \c GetProperty(). + /// + /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or + /// the empty string. Same namespace and prefix usage as \c GetProperty(). + /// + /// @return True if the qualifier exists. + + bool DoesQualifierExist ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName ) const; + + /// @} + + // ============================================================================================= + // Specialized Get and Set functions + // ============================================================================================= + + // --------------------------------------------------------------------------------------------- + /// \name Accessing properties as binary values. + /// @{ + /// + /// These are very similar to \c TXMPMeta::GetProperty() and \c TXMPMeta::SetProperty(), except + /// that the value is returned or provided in binary form instead of as a UTF-8 string. + /// \c TXMPUtils provides functions for converting between binary and string values. + /// Use the path composition functions in \c TXMPUtils to compose complex path expressions + /// for fields or items in nested structures or arrays, or for qualifiers. + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetProperty_Bool() retrieves the value of a Boolean property as a C++ bool. + /// + /// Reports whether a property exists, and retrieves its binary value and property type information. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue [out] A buffer in which to return the binary value. Can be null if the + /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have + /// values. + /// + /// @param options [out] A buffer in which to return the option flags describing the property, a + /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can + /// be null if flags are not wanted. + /// + /// @return True if the property exists. + + bool GetProperty_Bool ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetProperty_Int() retrieves the value of an integer property as a C long integer. + /// + /// Reports whether a property exists, and retrieves its binary value and property type information. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue [out] A buffer in which to return the binary value. Can be null if the + /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have + /// values. + /// + /// @param options [out] A buffer in which to return the option flags describing the property, a + /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can + /// be null if flags are not wanted. + /// + /// @return True if the property exists. + + bool GetProperty_Int ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetProperty_Int64() retrieves the value of an integer property as a C long long integer. + /// + /// Reports whether a property exists, and retrieves its binary value and property type information. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue [out] A buffer in which to return the binary value. Can be null if the + /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have + /// values. + /// + /// @param options [out] A buffer in which to return the option flags describing the property, a + /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can + /// be null if flags are not wanted. + /// + /// @return True if the property exists. + + bool GetProperty_Int64 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetProperty_Float() retrieves the value of a floating-point property as a C double float. + /// + /// Reports whether a property exists, and retrieves its binary value and property type information. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue [out] A buffer in which to return the binary value. Can be null if the + /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have + /// values. + /// + /// @param options [out] A buffer in which to return the option flags describing the property, a + /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can + /// be null if flags are not wanted. + /// + /// @return True if the property exists. + + bool GetProperty_Float ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetProperty_Date() retrieves the value of a date-time property as an \c #XMP_DateTime structure. + /// + /// Reports whether a property exists, and retrieves its binary value and property type information. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue [out] A buffer in which to return the binary value. Can be null if the + /// value is not wanted. Must be null for arrays and non-leaf levels of structs that do not have + /// values. + /// + /// @param options [out] A buffer in which to return the option flags describing the property, a + /// logical OR of allowed bit-flag constants; see \c #kXMP_PropValueIsStruct and following. Can + /// be null if flags are not wanted. + /// + /// @return True if the property exists. + + bool GetProperty_Date ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_DateTime * propValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty_Bool() sets the value of a Boolean property using a C++ bool. + /// + /// Sets a property with a binary value, creating it if necessary. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue The new binary value. Can be null if creating the property. Must be null + /// for arrays and non-leaf levels of structs that do not have values. + /// + /// @param options Option flags describing the property; a logical OR of allowed bit-flag + /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property + /// that already exists. + + void SetProperty_Bool ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool propValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty_Int() sets the value of an integer property using a C long integer. + /// + /// Sets a property with a binary value, creating it if necessary. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue The new binary value. Can be null if creating the property. Must be null + /// for arrays and non-leaf levels of structs that do not have values. + /// + /// @param options Option flags describing the property; a logical OR of allowed bit-flag + /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property + /// that already exists. + + void SetProperty_Int ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 propValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty_Int64() sets the value of an integer property using a C long long integer. + /// + /// Sets a property with a binary value, creating it if necessary. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue The new binary value. Can be null if creating the property. Must be null + /// for arrays and non-leaf levels of structs that do not have values. + /// + /// @param options Option flags describing the property; a logical OR of allowed bit-flag + /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property + /// that already exists. + + void SetProperty_Int64 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 propValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty_Float() sets the value of a floating-point property using a C double float. + /// + /// Sets a property with a binary value, creating it if necessary. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue The new binary value. Can be null if creating the property. Must be null + /// for arrays and non-leaf levels of structs that do not have values. + /// + /// @param options Option flags describing the property; a logical OR of allowed bit-flag + /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property + /// that already exists. + + void SetProperty_Float ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double propValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetProperty_Date() sets the value of a date/time property using an \c #XMP_DateTime structure. + /// + /// Sets a property with a binary value, creating it if necessary. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param propValue The new binary value. Can be null if creating the property. Must be null + /// for arrays and non-leaf levels of structs that do not have values. + /// + /// @param options Option flags describing the property; a logical OR of allowed bit-flag + /// constants; see \c #kXMP_PropValueIsStruct and following. Must match the type of a property + /// that already exists. + + void SetProperty_Date ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const XMP_DateTime & propValue, + XMP_OptionBits options = 0 ); + + /// @} + // ============================================================================================= + /// \name Accessing localized text (alt-text) properties. + /// @{ + /// + /// Localized text properties are stored in alt-text arrays. They allow multiple concurrent + /// localizations of a property value, for example a document title or copyright in several + /// languages. + /// + /// These functions provide convenient support for localized text properties, including a + /// number of special and obscure aspects. The most important aspect of these functions is that + /// they select an appropriate array item based on one or two RFC 3066 language tags. One of + /// these languages, the "specific" language, is preferred and selected if there is an exact + /// match. For many languages it is also possible to define a "generic" language that can be + /// used if there is no specific language match. The generic language must be a valid RFC 3066 + /// primary subtag, or the empty string. + /// + /// For example, a specific language of "en-US" should be used in the US, and a specific + /// language of "en-UK" should be used in England. It is also appropriate to use "en" as the + /// generic language in each case. If a US document goes to England, the "en-US" title is + /// selected by using the "en" generic language and the "en-UK" specific language. + /// + /// It is considered poor practice, but allowed, to pass a specific language that is just an + /// RFC 3066 primary tag. For example "en" is not a good specific language, it should only be + /// used as a generic language. Passing "i" or "x" as the generic language is also considered + /// poor practice but allowed. + /// + /// Advice from the W3C about the use of RFC 3066 language tags can be found at: + /// \li http://www.w3.org/International/articles/language-tags/ + /// + /// \note RFC 3066 language tags must be treated in a case insensitive manner. The XMP toolkit + /// does this by normalizing their capitalization: + /// \li The primary subtag is lower case, the suggested practice of ISO 639. + /// \li All 2 letter secondary subtags are upper case, the suggested practice of ISO 3166. + /// \li All other subtags are lower case. + /// + /// The XMP specification defines an artificial language, "x-default", that is used to + /// explicitly denote a default item in an alt-text array. The XMP toolkit normalizes alt-text + /// arrays such that the x-default item is the first item. The \c SetLocalizedText() function + /// has several special features related to the x-default item, see its description for details. + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetLocalizedText() retrieves information about a selected item in an alt-text array. + /// + /// The array item is selected according to these rules: + /// \li Look for an exact match with the specific language. + /// \li If a generic language is given, look for a partial match. + /// \li Look for an x-default item. + /// \li Choose the first item. + /// + /// A partial match with the generic language is where the start of the item's language matches + /// the generic string and the next character is '-'. An exact match is also recognized as a + /// degenerate case. + /// + /// You can pass "x-default" as the specific language. In this case, selection of an + /// \c x-default item is an exact match by the first rule, not a selection by the 3rd rule. The + /// last 2 rules are fallbacks used when the specific and generic languages fail to produce a + /// match. + /// + /// The return value reports whether a match was successfully made. + /// + /// @param schemaNS The namespace URI for the alt-text array; see \c GetProperty(). + /// + /// @param altTextName The name of the alt-text array. Can be a general path expression, must + /// not be null or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be + /// null or the empty string if no generic language is wanted. + /// + /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default". + /// Must not be null or the empty string. + /// + /// @param actualLang [out] A string object in which to return the language of the selected + /// array item, if an appropriate array item is found. Can be null if the language is not wanted. + /// + /// @param itemValue [out] A string object in which to return the value of the array item, if an + /// appropriate array item is found. Can be null if the value is not wanted. + /// + /// @param options A buffer in which to return the option flags that describe the array item, if + /// an appropriate array item is found. Can be null if the flags are not wanted. + /// + /// @return True if an appropriate array item exists. + + bool GetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + tStringObj * actualLang, + tStringObj * itemValue, + XMP_OptionBits * options ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetLocalizedText() modifies the value of a selected item in an alt-text array. + /// + /// Creates an appropriate array item if necessary, and handles special cases for the x-default + /// item. + /// + /// The array item is selected according to these rules: + /// \li Look for an exact match with the specific language. + /// \li If a generic language is given, look for a partial match. + /// \li Look for an x-default item. + /// \li Choose the first item. + /// + /// A partial match with the generic language is where the start of the item's language matches + /// the generic string and the next character is '-'. An exact match is also recognized as a + /// degenerate case. + /// + /// You can pass "x-default" as the specific language. In this case, selection of an + /// \c x-default item is an exact match by the first rule, not a selection by the 3rd rule. The + /// last 2 rules are fallbacks used when the specific and generic languages fail to produce a + /// match. + /// + /// Item values are modified according to these rules: + /// + /// \li If the selected item is from a match with the specific language, the value of that + /// item is modified. If the existing value of that item matches the existing value of the + /// x-default item, the x-default item is also modified. If the array only has 1 existing item + /// (which is not x-default), an x-default item is added with the given value. + /// + /// \li If the selected item is from a match with the generic language and there are no other + /// generic matches, the value of that item is modified. If the existing value of that item + /// matches the existing value of the x-default item, the x-default item is also modified. If + /// the array only has 1 existing item (which is not x-default), an x-default item is added + /// with the given value. + /// + /// \li If the selected item is from a partial match with the generic language and there are + /// other partial matches, a new item is created for the specific language. The x-default item + /// is not modified. + /// + /// \li If the selected item is from the last 2 rules then a new item is created for the + /// specific language. If the array only had an x-default item, the x-default item is also + /// modified. If the array was empty, items are created for the specific language and + /// x-default. + /// + /// @param schemaNS The namespace URI for the alt-text array; see \c GetProperty(). + /// + /// @param altTextName The name of the alt-text array. Can be a general path expression, must + /// not be null or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be + /// null or the empty string if no generic language is wanted. + /// + /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default". + /// Must not be null or the empty string. + /// + /// @param itemValue The new value for the matching array item, specified as a null-terminated + /// UTF-8 string. + /// + /// @param options Option flags, none currently defined. + + void SetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + XMP_StringPtr itemValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetLocalizedText() modifies the value of a selected item in an alt-text array using + /// a string object. + /// + /// Creates an appropriate array item if necessary, and handles special cases for the x-default + /// item. + /// + /// The array item is selected according to these rules: + /// \li Look for an exact match with the specific language. + /// \li If a generic language is given, look for a partial match. + /// \li Look for an x-default item. + /// \li Choose the first item. + /// + /// A partial match with the generic language is where the start of the item's language matches + /// the generic string and the next character is '-'. An exact match is also recognized as a + /// degenerate case. + /// + /// You can pass "x-default" as the specific language. In this case, selection of an \c x-default + /// item is an exact match by the first rule, not a selection by the 3rd rule. The last 2 rules + /// are fallbacks used when the specific and generic languages fail to produce a match. + /// + /// Item values are modified according to these rules: + /// + /// \li If the selected item is from a match with the specific language, the value of that + /// item is modified. If the existing value of that item matches the existing value of the + /// x-default item, the x-default item is also modified. If the array only has 1 existing item + /// (which is not x-default), an x-default item is added with the given value. + /// + /// \li If the selected item is from a match with the generic language and there are no other + /// generic matches, the value of that item is modified. If the existing value of that item + /// matches the existing value of the x-default item, the x-default item is also modified. If + /// the array only has 1 existing item (which is not x-default), an x-default item is added + /// with the given value. + /// + /// \li If the selected item is from a partial match with the generic language and there are + /// other partial matches, a new item is created for the specific language. The x-default item + /// is not modified. + /// + /// \li If the selected item is from the last 2 rules then a new item is created for the + /// specific language. If the array only had an x-default item, the x-default item is also + /// modified. If the array was empty, items are created for the specific language and + /// x-default. + /// + /// @param schemaNS The namespace URI for the alt-text array; see \c GetProperty(). + /// + /// @param altTextName The name of the alt-text array. Can be a general path expression, must + /// not be null or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be + /// null or the empty string if no generic language is wanted. + /// + /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default". + /// Must not be null or the empty string. + /// + /// @param itemValue The new value for the matching array item, specified as a string object. + /// + /// @param options Option flags, none currently defined. + + void SetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + const tStringObj & itemValue, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DeleteLocalizedText() deletes specific language alternatives from an alt-text array. + /// + /// The rules for finding the language value to delete are similar to those for \c #SetLocalizedText(). + /// + /// @param schemaNS The namespace URI for the alt-text array; see \c #GetProperty(). + /// + /// @param altTextName The name of the alt-text array. Can be a general path expression, must + /// not be null or the empty string; see \c #GetProperty() for namespace prefix usage. + /// + /// @param genericLang The name of the generic language as an RFC 3066 primary subtag. Can be + /// null or the empty string if no generic language is wanted. + /// + /// @param specificLang The name of the specific language as an RFC 3066 tag, or "x-default". + /// Must not be null or the empty string. + /// + void + DeleteLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang ); + + /// @} + + // ============================================================================================= + /// \name Creating and reading serialized RDF. + /// @{ + /// + /// The metadata contained in an XMP object must be serialized as RDF for storage in an XMP + /// packet and output to a file. Similarly, metadata in the form of serialized RDF (such as + /// metadata read from a file using \c TXMPFiles) must be parsed into an XMP object for + /// manipulation with the XMP Toolkit. + /// + /// These functions support parsing serialized RDF into an XMP object, and serializing an XMP + /// object into RDF. The input for parsing can be any valid Unicode encoding. ISO Latin-1 is + /// also recognized, but its use is strongly discouraged. Serialization is always as UTF-8. + + // --------------------------------------------------------------------------------------------- + /// @brief \c ParseFromBuffer() parses RDF from a series of input buffers into this XMP object. + /// + /// Use this to convert metadata from serialized RDF form (as, for example, read from an XMP + /// packet embedded in a file) into an XMP object that you can manipulate with the XMP Toolkit. + /// If this XMP object is empty and the input buffer contains a complete XMP packet, this is the + /// same as creating a new XMP object from that buffer with the constructor. + /// + /// You can use this function to combine multiple buffers into a single metadata tree. To + /// terminate an input loop conveniently, pass the option \c #kXMP_ParseMoreBuffers for all + /// real input, then make a final call with a zero length and \c #kXMP_NoOptions. The buffers + /// can be any length. The buffer boundaries need not respect XML tokens or even Unicode + /// characters. + /// + /// @param buffer A pointer to a buffer of input. Can be null if \c bufferSize is 0. + /// + /// @param bufferSize The length of the input buffer in bytes. Zero is a valid value. + /// + /// @param options An options flag that controls how the parse operation is performed. A logical + /// OR of these bit-flag constants: + /// \li \c #kXMP_ParseMoreBuffers - This is not the last buffer of input, more calls follow. + /// \li \c #kXMP_RequireXMPMeta - The \c x:xmpmeta XML element is required around \c rdf:RDF. + /// + /// @see \c TXMPFiles::GetXMP() + + void ParseFromBuffer ( XMP_StringPtr buffer, + XMP_StringLen bufferSize, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SerializeToBuffer() serializes metadata in this XMP object into a string as RDF. + /// + /// Use this to prepare metadata for storage as an XMP packet embedded in a file. See \c TXMPFiles::PutXMP(). + /// + /// @param rdfString [out] A string object in which to return the serialized RDF. Must not be null. + /// + /// @param options An options flag that controls how the serialization operation is performed. + /// The specified options must be logically consistent; an exception is thrown if they are not. + /// A logical OR of these bit-flag constants: + /// \li \c kXMP_OmitPacketWrapper - Do not include an XML packet wrapper. This cannot be + /// specified together with \c #kXMP_ReadOnlyPacket, \c #kXMP_IncludeThumbnailPad, or + /// \c #kXMP_ExactPacketLength. + /// \li \c kXMP_ReadOnlyPacket - Create a read-only XML packet wapper. Cannot be specified + /// together with \c kXMP_OmitPacketWrapper. + /// \li \c kXMP_UseCompactFormat - Use a highly compact RDF syntax and layout. + /// \li \c kXMP_IncludeThumbnailPad - Include typical space for a JPEG thumbnail in the + /// padding if no \c xmp:Thumbnails property is present. Cannot be specified together with + /// \c kXMP_OmitPacketWrapper. + /// \li \c kXMP_ExactPacketLength - The padding parameter provides the overall packet length. + /// The actual amount of padding is computed. An exception is thrown if the packet exceeds + /// this length with no padding. Cannot be specified together with + /// \c kXMP_OmitPacketWrapper. + /// + /// In addition to the above options, you can include one of the following encoding options: + /// \li \c #kXMP_EncodeUTF8 - Encode as UTF-8, the default. + /// \li \c #kXMP_EncodeUTF16Big - Encode as big-endian UTF-16. + /// \li \c #kXMP_EncodeUTF16Little - Encode as little-endian UTF-16. + /// \li \c #kXMP_EncodeUTF32Big - Encode as big-endian UTF-32. + /// \li \c #kXMP_EncodeUTF32Little - Encode as little-endian UTF-32. + /// + /// @param padding The amount of padding to be added if a writeable XML packet is created. If + /// zero (the default) an appropriate amount of padding is computed. + /// + /// @param newline The string to be used as a line terminator. If empty, defaults to linefeed, + /// U+000A, the standard XML newline. + /// + /// @param indent The string to be used for each level of indentation in the serialized RDF. If + /// empty, defaults to two ASCII spaces, U+0020. + /// + /// @param baseIndent The number of levels of indentation to be used for the outermost XML + /// element in the serialized RDF. This is convenient when embedding the RDF in other text. + + void SerializeToBuffer ( tStringObj * rdfString, + XMP_OptionBits options, + XMP_StringLen padding, + XMP_StringPtr newline, + XMP_StringPtr indent = "", + XMP_Index baseIndent = 0 ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c SerializeToBuffer() serializes metadata in this XMP object into a string as RDF. + /// + /// This simpler form of the function uses default values for the \c newline, \c indent, and + /// \c baseIndent parameters. + /// + /// @param rdfString [out] A string object in which to return the serialized RDF. Must not be null. + /// + /// @param options An options flag that controls how the serialization operation is performed. + /// The specified options must be logically consistent; an exception is thrown if they are not. + /// A logical OR of these bit-flag constants: + /// \li \c kXMP_OmitPacketWrapper - Do not include an XML packet wrapper. This cannot be + /// specified together with \c #kXMP_ReadOnlyPacket, \c #kXMP_IncludeThumbnailPad, or + /// \c #kXMP_ExactPacketLength. + /// \li \c kXMP_ReadOnlyPacket - Create a read-only XML packet wapper. Cannot be specified + /// together with \c kXMP_OmitPacketWrapper. + /// \li \c kXMP_UseCompactFormat - Use a highly compact RDF syntax and layout. + /// \li \c kXMP_IncludeThumbnailPad - Include typical space for a JPEG thumbnail in the + /// padding if no \c xmp:Thumbnails property is present. Cannot be specified together with + /// \c kXMP_OmitPacketWrapper. + /// \li \c kXMP_ExactPacketLength - The padding parameter provides the overall packet length. + /// The actual amount of padding is computed. An exception is thrown if the packet exceeds + /// this length with no padding. Cannot be specified together with + /// \c kXMP_OmitPacketWrapper. + /// + /// In addition to the above options, you can include one of the following encoding options: + /// \li \c #kXMP_EncodeUTF8 - Encode as UTF-8, the default. + /// \li \c #kXMP_EncodeUTF16Big - Encode as big-endian UTF-16. + /// \li \c #kXMP_EncodeUTF16Little - Encode as little-endian UTF-16. + /// \li \c #kXMP_EncodeUTF32Big - Encode as big-endian UTF-32. + /// \li \c #kXMP_EncodeUTF32Little - Encode as little-endian UTF-32. + /// + /// @param padding The amount of padding to be added if a writeable XML packet is created. + /// If zero (the default) an appropriate amount of padding is computed. + + void SerializeToBuffer ( tStringObj * rdfString, + XMP_OptionBits options = 0, + XMP_StringLen padding = 0 ) const; + + /// @} + // ============================================================================================= + // Miscellaneous Member Functions + // ============================== + + // --------------------------------------------------------------------------------------------- + /// \name Helper functions. + /// @{ + + // --------------------------------------------------------------------------------------------- + /// @brief Retrieves an internal reference that can be safely passed across DLL boundaries and + /// reconstructed. + /// + /// The \c TXMPMeta class is a normal C++ template, it is instantiated and local to each client + /// executable, as are the other \c TXMP* classes. Different clients might not use the same + /// string type to instantiate \c TXMPMeta. + /// + /// Because of this you should not pass \c SXMPMeta objects, or pointers to \c SXMPMeta objects, + /// across DLL boundaries. Use this function to obtain a safe internal reference that you can + /// pass, then construct a local object on the callee side. This construction does not create a + /// cloned XMP tree, it is the same underlying XMP object safely wrapped in each client's + /// \c SXMPMeta object. + /// + /// Use this function and the associated constructor like this: + /// \li The callee's header contains: + ///
+    /// CalleeMethod ( XMPMetaRef xmpRef );
+    /// 
+ /// + /// \li The caller's code contains: + ///
+    /// SXMPMeta callerXMP;
+    /// CalleeMethod ( callerXMP.GetInternalRef() );
+    /// 
+ /// + /// \li The callee's code contains: + ///
+    /// SXMPMeta calleeXMP ( xmpRef );
+    /// 
+ /// + /// @return The reference object. + + XMPMetaRef GetInternalRef() const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c GetObjectName() retrieves the client-assigned name of this XMP object. + /// + /// Assign this name with \c SetObjectName(). + /// + /// @param name [out] A string object in which to return the name. + + void GetObjectName ( tStringObj * name ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetObjectName() assigns a name to this XMP object. + /// + /// Retrieve this client-assigned name with \c GetObjectName(). + /// + /// @param name The name as a null-terminated UTF-8 string. + + void SetObjectName ( XMP_StringPtr name ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetObjectName() assigns a name to this XMP object. + /// + /// Retrieve this client-assigned name with \c GetObjectName(). + /// + /// @param name The name as a string object. + + void SetObjectName ( tStringObj name ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c Sort() sorts the data model tree of an XMP object. + /// + /// Use this function to sort the data model of an XMP object into a canonical order. This can + /// be convenient when comparing data models, (e.g. by text comparison of DumpObject output). + /// + /// At the top level the namespaces are sorted by their prefixes. Within a namespace, the top + /// level properties are sorted by name. Within a struct, the fields are sorted by their + /// qualified name, i.e. their XML prefix:local form. Unordered arrays of simple items are + /// sorted by value. Language Alternative arrays are sorted by the xml:lang qualifiers, with + /// the "x-default" item placed first. + + void Sort(); + + // --------------------------------------------------------------------------------------------- + /// @brief \c Erase() restores the object to a "just constructed" state. + + void Erase(); + + // --------------------------------------------------------------------------------------------- + /// @brief \c Clone() creates a deep copy of an XMP object. + /// + /// Use this function to copy an entire XMP metadata tree. Assignment and copy constructors only + /// increment a reference count, they do not do a deep copy. This function returns an object, + /// not a pointer. The following shows correct usage: + /// + ///
+    /// SXMPMeta * clone1 = new SXMPMeta ( sourceXMP.Clone() );  // This works.
+    /// SXMPMeta   clone2 ( sourceXMP.Clone );  	// This works also. (Not a pointer.)
+    /// 
+ /// The \c clone2 example does not use an explicit pointer. + /// This is good for local usage, protecting against memory leaks. + /// + /// This is an example of incorrect usage: + ///
+    /// SXMPMeta * clone3 = &sourceXMP.Clone();		// ! This does not work!
+    /// 
+ /// The assignment to \c clone3 creates a temporary object, initializes it with the clone, + /// assigns the address of the temporary to \c clone3, then deletes the temporary. + /// + /// @param options Option flags, not currently defined.. + /// + /// @return An XMP object cloned from the original. + + TXMPMeta Clone ( XMP_OptionBits options = 0 ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c CountArrayItems() reports the number of items currently defined in an array. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @return The number of items. + + XMP_Index CountArrayItems ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief \c DumpObject() outputs the content of an XMP object to a callback handler for debugging. + /// + /// Invokes a client-defined callback for each line of output. + /// + /// @param outProc The client-defined procedure to handle each line of output. + /// + /// @param clientData A pointer to client-defined data to pass to the handler. + /// + /// @return A success-fail status value, returned from the handler. Zero is success, failure + /// values are client-defined. + /// + /// @see Static function \c DumpNamespaces() + + XMP_Status DumpObject ( XMP_TextOutputProc outProc, + void * clientData ) const; + + // --------------------------------------------------------------------------------------------- + /// @brief Not implemented + XMP_OptionBits GetObjectOptions() const; + + // --------------------------------------------------------------------------------------------- + /// \brief Not implemented + void SetObjectOptions ( XMP_OptionBits options ); + + /// @} + + // ============================================================================================= + // Error notifications + // =================== + + // --------------------------------------------------------------------------------------------- + /// \name Error notifications + /// @{ + /// + /// From the beginning through version 5.5, XMP Tookit errors result in throwing an \c XMP_Error + /// exception. For the most part exceptions were thrown early and thus API calls aborted as soon + /// as an error was detected. Starting in version 5.5, support has been added for notifications + /// of errors arising in calls to \c TXMPMeta functions. + /// + /// A client can register an error notification callback function for a \c TXMPMeta object. This + /// can be done as a global default or individually to each object. The global default applies + /// to all objects created after it is registered. Within the object there is no difference + /// between the global default or explicitly registered callback. The callback function returns + /// a \c bool value indicating if recovery should be attempted (true) or an exception thrown + /// (false). If no callback is registered, a best effort at recovery and continuation will be + /// made with an exception thrown if recovery is not possible. + /// + /// The number of notifications delivered for a given TXMPMeta object can be limited. This is + /// intended to reduce chatter from multiple or cascading errors. The limit is set when the + /// callback function is registered. This limits the number of notifications of the highest + /// severity delivered or less. If a higher severity error occurs, the counting starts again. + /// The limit and counting can be reset at any time, see \c ResetErrorCallbackLimit. + + // -------------------------------------------------------------------------------------------- + /// @brief SetDefaultErrorCallback() registers a global default error notification callback. + /// + /// @param proc The client's callback function. + /// + /// @param context Client-provided context for the callback. + /// + /// @param limit A limit on the number of notifications to be delivered. + + static void SetDefaultErrorCallback ( XMPMeta_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 ); + + // -------------------------------------------------------------------------------------------- + /// @brief SetErrorCallback() registers an error notification callback. + /// + /// @param proc The client's callback function. + /// + /// @param context Client-provided context for the callback. + /// + /// @param limit A limit on the number of notifications to be delivered. + + void SetErrorCallback ( XMPMeta_ErrorCallbackProc proc, void* context = 0, XMP_Uns32 limit = 1 ); + + // -------------------------------------------------------------------------------------------- + /// @brief ResetErrorCallbackLimit() resets the error notification limit and counting. It has no + /// effect if an error notification callback function is not registered. + /// + /// @param limit A limit on the number of notifications to be delivered. + + void ResetErrorCallbackLimit ( XMP_Uns32 limit = 1 ); + + /// @} + + // ============================================================================================= + + XMPMetaRef xmpRef; // *** Should be private, see below. + +private: + +#if 0 // *** VS.Net and gcc seem to not handle the friend declarations properly. + friend class TXMPIterator ; + friend class TXMPUtils ; +#endif + + static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ); + +}; // class TXMPMeta + +#endif // __TXMPMeta_hpp__ diff --git a/source/lib/xmp_core/public/include/TXMPUtils.hpp b/source/lib/xmp_core/public/include/TXMPUtils.hpp new file mode 100644 index 0000000..79ade07 --- /dev/null +++ b/source/lib/xmp_core/public/include/TXMPUtils.hpp @@ -0,0 +1,967 @@ +#ifndef __TXMPUtils_hpp__ +#define __TXMPUtils_hpp__ 1 + +#if ( ! __XMP_hpp__ ) + #error "Do not directly include, use XMP.hpp" +#endif + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================= +/// \file TXMPUtils.hpp +/// \brief API for access to the XMP Toolkit utility services. +/// +/// \c TXMPUtils is the template class providing utility services for the XMP Toolkit. It must be +/// instantiated with a string class such as \c std::string. See the instructions in XMP.hpp, and +/// the Overview for a discussion of the overall architecture of the XMP API. +// ================================================================================================= + +// ================================================================================================= +/// \class TXMPUtils TXMPUtils.hpp +/// @brief API for access to the XMP Toolkit utility services. +/// +/// \c TXMPUtils is a template class which must be instantiated with a string class such as +/// \c std::string. See the instructions in XMP.hpp, and the Overview for a discussion of the overall +/// architecture of the XMP API. +/// +/// This class defines helper functions that support the basic metadata manipulation provided by +/// \c TXMPMeta. All of the functions are static; that is, you call them directly from the concrete +/// class (\c SXMPUtils), which is never itself instantiated. +/// +/// General categories of utilities include: +/// +/// \li Composing complex path expressions, which you can then pass to the property access +/// functions in \c TXMPMeta +/// \li Converting between binary and string forms of property values +/// \li Manipulating date/time values +/// \li Encoding and decoding base-64 strings +/// \li JPEG file handling +/// \li Editing aids for creating a user interface for the XMP Toolkit +// ================================================================================================= + +template class TXMPUtils { + +public: + + // ============================================================================================= + // No constructors or destructor declared or needed + // ================================================ + + // ============================================================================================ + /// \name Path composition + /// @{ + /// + /// These functions provide support for composing path expressions to deeply nested properties. + /// The functions in \c TXMPMeta such as \c TXMPMeta::GetProperty(), + /// \c TXMPMeta::GetArrayItem(), and \c TXMPMeta::GetStructField() provide easy access to top level + /// simple properties, items in top level arrays, and fields of top level structs. They are + /// not as convenient for more complex things, such as fields several levels deep in a complex + /// struct, or fields within an array of structs, or items of an array that is a field of a + /// struct. You can use these utility functions to compose these paths, which you can then pass + /// to the property access functions. You can also compose paths to top-level array items or + /// struct fields so that you can use the binary accessors such as + /// \c TXMPMeta::GetProperty_Int(). + /// + /// You can use these functions is to compose a complete path expression, or all but the last + /// component. For example, suppose you have a property that is an array of integers within a + /// struct. You can access one of the array items like this: + /// + ///
+    ///   SXMPUtils::ComposeStructFieldPath ( schemaNS, "Struct", fieldNS, "Array", &path );
+    ///   SXMPUtils::ComposeArrayItemPath ( schemaNS, path, index, &path );
+    ///   exists = xmpObj.GetProperty_Int ( schemaNS, path, &value, &options );
+    /// 
+ /// + /// You could also use this code if you want the string form of the integer: + /// + ///
+    ///   SXMPUtils::ComposeStructFieldPath ( schemaNS, "Struct", fieldNS, "Array", &path );
+    ///   xmpObj.GetArrayItem ( schemaNS, path, index, &value, &options );
+    /// 
+ /// + /// \note It might look confusing that the \c schemaNS is passed in all of the calls above. This + /// is because the XMP Toolkit keeps the top-level "schema" namespace separate from the rest of + /// the path expression. + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeArrayItemPath() composes the path expression for an item in an array. + /// + /// The returned string is in the form ns:arrayName[i], where "ns" is the prefix for + /// the specified namespace, and "i" is the decimal representation of specified item index. + /// If the last item was specified, the path is ns:arrayName[last()]. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param itemIndex The 1-based index of the desired item. Use the macro + /// \c #kXMP_ArrayLastItem to specify the last existing array item. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeArrayItemPath ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + tStringObj * fullPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeStructFieldPath() composes the path expression for a field in a struct. + /// + /// The returned string is in the form ns:structName/fNS:fieldName, where "ns" is the + /// prefix for the schema namespace, and "fNS" is the prefix for field namespace. + /// + /// @param schemaNS The namespace URI for the struct; see \c GetProperty(). + /// + /// @param structName The name of the struct. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field. Same URI and prefix usage as the + /// \c schemaNS and \c structName parameters. + /// + /// @param fieldName The name of the field. Must be a single XML name, must not be null or the + /// empty string. Same URI and prefix usage as the \c schemaNS and \c structName parameters. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeStructFieldPath ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + tStringObj * fullPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeQualifierPath() composes the path expression for a qualifier. + /// + /// The returned string is in the form ns:propName/?qNS:qualName, where "ns" is the + /// prefix for the schema namespace, and "qNS" is the prefix for the qualifier namespace. + /// + /// @param schemaNS The namespace URI; see \c GetProperty(). + /// + /// @param propName The name of the property to which the qualifier is attached. Can be a + /// general path expression, must not be null or the empty string; see \c GetProperty() for + /// namespace prefix usage. + /// + /// @param qualNS The namespace URI for the qualifier. Same URI and prefix usage as the + /// \c schemaNS and \c propName parameters. + /// + /// @param qualName The name of the qualifier. Must be a single XML name, must not be null or the + /// empty string. Same URI and prefix usage as the \c schemaNS and \c propName parameters. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeQualifierPath ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + tStringObj * fullPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeLangSelector() composes the path expression to select an alternate item by language. + /// + /// Path syntax allows two forms of "content addressing" to select an item in an array of + /// alternatives. The form used in this function lets you select an item in an alt-text array + /// based on the value of its \c xml:lang qualifier. The other form of content addressing is + /// shown in \c ComposeFieldSelector(). + /// + /// The returned string is in the form ns:arrayName[\@xml:lang='langName'], where + /// "ns" is the prefix for the schema namespace + /// + /// This function provides a path expression that is explicitly and only for a specific + /// language. In most cases, \c TXMPMeta::SetLocalizedText() and \c TXMPMeta::GetLocalizedText() + /// are preferred, because they provide extra logic to choose the appropriate language and + /// maintain consistency with the 'x-default' value. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param langName The RFC 3066 code for the desired language, as a null-terminated UTF-8 string. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeLangSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr langName, + tStringObj * fullPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeLangSelector() composes a path expression to select an alternate item by language. + /// + /// Path syntax allows two forms of "content addressing" to select an item in an array of + /// alternatives. The form used in this function lets you select an item in an alt-text array + /// based on the value of its \c xml:lang qualifier. The other form of content addressing is + /// shown in \c ComposeFieldSelector(). + /// + /// The returned string is in the form ns:arrayName[\@xml:lang='langName'], where + /// "ns" is the prefix for the schema namespace + /// + /// This function provides a path expression that is explicitly and only for a specific + /// language. In most cases, \c TXMPMeta::SetLocalizedText() and \c TXMPMeta::GetLocalizedText() + /// are preferred, because they provide extra logic to choose the appropriate language and + /// maintain consistency with the 'x-default' value. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param langName The RFC 3066 code for the desired language, as a string object. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeLangSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + const tStringObj & langName, + tStringObj * fullPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeFieldSelector() composes a path expression to select an alternate item by a field's value. + /// + /// Path syntax allows two forms of "content addressing" to select an item in an array of + /// alternatives. The form used in this function lets you select an item in an array of structs + /// based on the value of one of the fields in the structs. The other form of content addressing + /// is shown in \c ComposeLangSelector(). + /// + /// For example, consider a simple struct that has two fields, the name of a city and the URI of + /// an FTP site in that city. Use this to create an array of download alternatives. You can show + /// the user a popup built from the values of the city fields, then get the corresponding URI as + /// follows: + ///
+    ///   ComposeFieldSelector ( schemaNS, "Downloads", fieldNS, "City", chosenCity, &path );
+    ///   exists = GetStructField ( schemaNS, path, fieldNS, "URI", &uri );
+    /// 
+ /// + /// The returned string is in the form ns:arrayName[fNS:fieldName='fieldValue'], where + /// "ns" is the prefix for the schema namespace and "fNS" is the prefix for the field namespace. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field used as the selector. Same URI and prefix + /// usage as the \c schemaNS and \c arrayName parameters. + /// + /// @param fieldName The name of the field used as the selector. Must be a single XML name, must + /// not be null or the empty string. It must be the name of a field that is itself simple. + /// + /// @param fieldValue The desired value of the field, specified as a null-terminated UTF-8 string. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeFieldSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + tStringObj * fullPath ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ComposeFieldSelector() composes a path expression to select an alternate item by a field's value. + /// + /// Path syntax allows two forms of "content addressing" to select an item in an array of + /// alternatives. The form used in this function lets you select an item in an array of structs + /// based on the value of one of the fields in the structs. The other form of content addressing + /// is shown in \c ComposeLangSelector(). + /// + /// For example, consider a simple struct that has two fields, the name of a city and the URI of + /// an FTP site in that city. Use this to create an array of download alternatives. You can show + /// the user a popup built from the values of the city fields, then get the corresponding URI as + /// follows: + ///
+    ///   ComposeFieldSelector ( schemaNS, "Downloads", fieldNS, "City", chosenCity, &path );
+    ///   exists = GetStructField ( schemaNS, path, fieldNS, "URI", &uri );
+    /// 
+ /// + /// The returned string is in the form ns:arrayName[fNS:fieldName='fieldValue'], where + /// "ns" is the prefix for the schema namespace and "fNS" is the prefix for the field namespace. + /// + /// @param schemaNS The namespace URI for the array; see \c GetProperty(). + /// + /// @param arrayName The name of the array. Can be a general path expression, must not be null + /// or the empty string; see \c GetProperty() for namespace prefix usage. + /// + /// @param fieldNS The namespace URI for the field used as the selector. Same URI and prefix + /// usage as the \c schemaNS and \c arrayName parameters. + /// + /// @param fieldName The name of the field used as the selector. Must be a single XML name, must + /// not be null or the empty string. It must be the name of a field that is itself simple. + /// + /// @param fieldValue The desired value of the field, specified as a string object. + /// + /// @param fullPath [out] A string in which to return the composed path. + + static void ComposeFieldSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + const tStringObj & fieldValue, + tStringObj * fullPath ); + + /// @} + + // ============================================================================================= + /// \name Conversion between binary types and strings + /// @{ + /// + /// The main accessors in \c TXMPMeta set and retrieve property values as strings. additional + /// functions, such as \c TXMPMeta::SetPropertyInt(), set and retrieve property values as + /// explicit binary data types. Use these functions to convert between binary and string + /// values. + /// + /// Strings can be specified as null-terminated UTF-8 (\c #XMP_StringPtr), or as string + /// objects (\c tStringObj) of the type declared when instantiating the XMP classes; see + /// \c XMP.hpp. Alternate forms of each conversion function allow either type of string. + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertFromBool() converts a Boolean value to a string. + /// + /// The string values of Booleans are returned by the macros \c #kXMP_TrueStr and + /// \c #kXMP_FalseStr in \c XMP_Const.h. + /// + /// @param binValue The Boolean value to be converted. + /// + /// @param strValue [out] A buffer in which to return the string representation of the value. + + static void ConvertFromBool ( bool binValue, + tStringObj * strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertFromInt() converts a 32-bit integer value to a string. + /// + /// @param binValue The integer value to be converted. + /// + /// @param format Optional. A C \c sprintf format for the conversion. Default is "%d". + /// + /// @param strValue [out] A buffer in which to return the string representation of the value. + + static void ConvertFromInt ( long binValue, + XMP_StringPtr format, + tStringObj * strValue ); + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertFromInt64() converts a 64-bit integer value to a string. + /// + /// @param binValue The integer value to be converted. + /// + /// @param format Optional. A C \c sprintf format for the conversion. Default is "%d". + /// + /// @param strValue [out] A buffer in which to return the string representation of the value. + + static void ConvertFromInt64 ( long long binValue, + XMP_StringPtr format, + tStringObj * strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertFromFloat() converts a floating-point value to a string. + /// + /// @param binValue The floating-point value to be converted. + /// + /// @param format Optional. A C \c sprintf format for the conversion. Default is "%d". + /// + /// @param strValue [out] A buffer in which to return the string representation of the value. + + static void ConvertFromFloat ( double binValue, + XMP_StringPtr format, + tStringObj * strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertFromDate() converts a date/time value to a string. + /// + /// Formats a date according to the ISO 8601 profile in http://www.w3.org/TR/NOTE-datetime: + ///
+    ///   YYYY
+    ///   YYYY-MM
+    ///   YYYY-MM-DD
+    ///   YYYY-MM-DDThh:mmTZD
+    ///   YYYY-MM-DDThh:mm:ssTZD
+    ///   YYYY-MM-DDThh:mm:ss.sTZD
+    /// 
+ /// + /// \c YYYY = four-digit year, formatted as "%.4d"
+ /// \c MM = two-digit month (01=January)
+ /// \c DD = two-digit day of month (01 through 31)
+ /// \c hh = two digits of hour (00 through 23)
+ /// \c mm = two digits of minute (00 through 59)
+ /// \c ss = two digits of second (00 through 59)
+ /// \c s = one or more digits representing a decimal fraction of a second
+ /// \c TZD = time zone designator (Z or +hh:mm or -hh:mm) + /// + /// Time-only input is allowed where the year, month, and day are all zero. This is output as + /// "0000-00-00...". + /// + /// @note ISO 8601 does not allow years less than 1000 or greater than 9999. This API allows + /// any year, even negative ones. The W3C profile also requires a time zone designator if a time + /// is present, this API treats the time zone designator as optional. The XMP_DateTime type has + /// an explicit notion of zone-less time. + /// + /// @param binValue The date/time value to be converted. + /// + /// @param strValue [out] A buffer in which to return the ISO 8601 string representation of the date/time. + + static void ConvertFromDate ( const XMP_DateTime & binValue, + tStringObj * strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToBool() converts a string to a Boolean value. + /// + /// The preferred strings are those returned by the macros \c #kXMP_TrueStr and \c #kXMP_FalseStr. + /// If these do not match, the function does a case insensitive comparison, then simply 't' or 'f', + /// and finally non-zero and zero integer representations. + /// + /// @param strValue The string representation of the value, specified as a null-terminated UTF-8 string. + /// + /// @return The appropriate C++ bool value for the string. + + static bool ConvertToBool ( XMP_StringPtr strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToBool() converts a string to a Boolean value. + /// + /// Overloads the basic form of the function, allowing you to pass a string object, + /// rather than a const * char. It is otherwise identical; see details in the canonical form. + /// + /// @param strValue The string representation of the value, specified as a string object. + /// + /// @return The appropriate C++ bool value for the string. + + static bool ConvertToBool ( const tStringObj & strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToInt() converts a string to a 32-bit integer value. + /// + /// @param strValue The string representation of the value, specified as a null-terminated UTF-8 string. + /// + /// @return The 32-bit integer value. + + static long ConvertToInt ( XMP_StringPtr strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToInt() converts a string to a 32-bit integer value. + /// + /// Overloads the basic form of the function, allowing you to pass a string object, + /// rather than a const * char. It is otherwise identical. + /// + /// @param strValue The string representation of the value, specified as a string object. + /// + /// @return The 32-bit integer value. + + static long ConvertToInt ( const tStringObj & strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToInt64() converts a string to a 64-bit integer value. + /// + /// @param strValue The string representation of the value, specified as a null-terminated UTF-8 string. + /// + /// @return The 64-bit integer value. + + static long long ConvertToInt64 ( XMP_StringPtr strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToInt64() converts a string to a 64-bit integer value. + /// + /// Overloads the basic form of the function, allowing you to pass a string object, + /// rather than a const * char. It is otherwise identical. + /// + /// @param strValue The string representation of the value, specified as a string object. + /// + /// @return The 64-bit integer value. + + static long long ConvertToInt64 ( const tStringObj & strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToFloat() converts a string to a floating-point value. + /// + /// @param strValue The string representation of the value, specified as a null-terminated UTF-8 string. + /// + /// @return The floating-point value. + + static double ConvertToFloat ( XMP_StringPtr strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToFloat() converts a string to a floating-point value. + /// + /// Overloads the basic form of the function, allowing you to pass a string object, + /// rather than a const * char. It is otherwise identical. + /// + /// @param strValue The string representation of the value, specified as a string object. + /// + /// @return The floating-point value. + + static double ConvertToFloat ( const tStringObj & strValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToDate() converts a string to a date/time value. + /// + /// Parses a date according to the ISO 8601 profile in http://www.w3.org/TR/NOTE-datetime: + ///
+    ///   YYYY
+    ///   YYYY-MM
+    ///   YYYY-MM-DD
+    ///   YYYY-MM-DDThh:mmTZD
+    ///   YYYY-MM-DDThh:mm:ssTZD
+    ///   YYYY-MM-DDThh:mm:ss.sTZD
+    /// 
+ /// + /// \c YYYY = four-digit year, formatted as "%.4d"
+ /// \c MM = two-digit month (01=January)
+ /// \c DD = two-digit day of month (01 through 31)
+ /// \c hh = two digits of hour (00 through 23)
+ /// \c mm = two digits of minute (00 through 59)
+ /// \c ss = two digits of second (00 through 59)
+ /// \c s = one or more digits representing a decimal fraction of a second
+ /// \c TZD = time zone designator (Z or +hh:mm or -hh:mm) + /// + /// A missing date portion or missing TZD are tolerated. A missing date value can begin with + /// "Thh:" or "hh:"; the year, month, and day are all set to zero in the \c #XMP_DateTime value. + /// A missing TZD is assumed to be UTC. + /// + /// @note ISO 8601 does not allow years less than 1000 or greater than 9999. This API allows + /// any year, even negative ones. The W3C profile also requires a time zone designator if a time + /// is present, this API treats the time zone designator as optional. The XMP_DateTime type has + /// an explicit notion of zone-less time. + /// + /// @param strValue The ISO 8601 string representation of the date/time, specified as a + /// null-terminated UTF-8 string. + /// + /// @param binValue [out] A buffer in which to return the binary date/time value. + + static void ConvertToDate ( XMP_StringPtr strValue, + XMP_DateTime * binValue ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToDate() converts a string to a date/time value. + /// + /// Overloads the basic form of the function, allowing you to pass a string object, + /// rather than a const * char. It is otherwise identical. + /// See details for the canonical form. + /// + /// + /// @param strValue The ISO 8601 string representation of the date/time, specified as a string + /// object. + /// + /// @param binValue [out] A buffer in which to return the binary date/time value. + + static void ConvertToDate ( const tStringObj & strValue, + XMP_DateTime * binValue ); + + /// @} + + // ============================================================================================= + /// \name Date-time manipulation + /// @{ + /// + /// In addition to the type-conversion functions that convert between strings and binary + /// date-time values, these functions create, manipulate, and compare date-time values. + + // --------------------------------------------------------------------------------------------- + /// @brief \c CurrentDateTime() obtains the current date and time. + /// + /// Creates and returns a binary \c #XMP_DateTime value. The returned time is UTC, properly + /// adjusted for the local time zone. The resolution of the time is not guaranteed to be finer + /// than seconds. + /// + /// @param time [out] A buffer in which to return the date/time value. + + static void CurrentDateTime ( XMP_DateTime * time ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SetTimeZone() sets the time zone in a date/time value to the local time zone. + /// + /// Any existing time zone value is replaced. The other date/time fields are not adjusted in any way. + /// + /// @param time A pointer to the date-time value, which is modified in place. + + static void SetTimeZone ( XMP_DateTime * time ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToUTCTime() ensures that a time is UTC. + /// + /// If the time zone is not UTC, the time is adjusted and the time zone set to be UTC. The value + /// is not modified if the time zone is already UTC or if the value has no time zone. + /// + /// @param time A pointer to the date-time value, which is modified in place. + + static void ConvertToUTCTime ( XMP_DateTime * time ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c ConvertToLocalTime() ensures that a time is local. + /// + /// If the time zone is not the local zone, the time is adjusted and the time zone set to be local. + /// The value is not modified if the time zone is already the local zone or if the value has no + /// time zone. + /// + /// @param time A pointer to the date-time value, which is modified in place. + + static void ConvertToLocalTime ( XMP_DateTime * time ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c CompareDateTime() compares the order of two date/time values. + /// + /// Both values are treated as in the same time zone if either has no time zone. + /// + /// @param left The left-side date/time value. + /// + /// @param right The right-side date/time value. + /// + /// @return An integer indicating the order: + /// \li -1 if left is earlier than right + /// \li 0 if left matches right + /// \li +1 if left is later than right + + static int CompareDateTime ( const XMP_DateTime & left, + const XMP_DateTime & right ); + + /// @} + + // ============================================================================================= + /// \name Base64 encoding and decoding + /// @{ + /// + /// These functions convert between raw data values and Base64-encoded strings. + + // --------------------------------------------------------------------------------------------- + /// @brief \c EncodeToBase64() converts a raw data value to a Base64-encoded string. + /// + /// @param rawStr An \c #XMP_StringPtr (char *) string containing the raw data to be converted. + /// + /// @param rawLen The number of characters of raw data to be converted. + /// + /// @param encodedStr [out] A string object in which to return the encoded string. + + static void EncodeToBase64 ( XMP_StringPtr rawStr, + XMP_StringLen rawLen, + tStringObj * encodedStr ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c EncodeToBase64() converts a raw data value passed in a string object to a Base64-encoded string. + /// + /// Overloads the basic form of the function, allowing you to pass a string object as input. + /// It is otherwise identical. + /// + /// @param rawStr A string object containing the raw data to be converted. + /// + /// @param encodedStr [out] A string object in which to return the encoded string. + + static void EncodeToBase64 ( const tStringObj & rawStr, + tStringObj * encodedStr ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DecodeFromBase64() Decodes a Base64-encoded string to raw data. + /// + /// @param encodedStr An \c #XMP_StringPtr (char *) string containing the encoded data to be converted. + /// + /// @param encodedLen The number of characters of raw data to be converted. + /// + /// @param rawStr [out] A string object in which to return the decoded data. + + static void DecodeFromBase64 ( XMP_StringPtr encodedStr, + XMP_StringLen encodedLen, + tStringObj * rawStr ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DecodeFromBase64() Decodes a Base64-encoded string, passed as a string object, to raw data. + /// + /// Overloads the basic form of the function, allowing you to pass a string object as input. + /// It is otherwise identical. + /// + /// @param encodedStr An string object containing the encoded data to be converted. + /// + /// @param rawStr [out] A string object in which to return the decoded data. + + static void DecodeFromBase64 ( const tStringObj & encodedStr, + tStringObj * rawStr ); + + /// @} + + // ============================================================================================= + // ============================================================================================= + /// \name JPEG file handling + /// @{ + /// + /// These functions support the partitioning of XMP in JPEG files into standard and extended + /// portions in order to work around the 64KB size limit of JPEG marker segments. + /// + /// @note (Doc note) Add detail about how to write out and read back extended data + + // --------------------------------------------------------------------------------------------- + /// @brief \c PackageForJPEG() creates XMP serializations appropriate for a JPEG file. + /// + /// The standard XMP in a JPEG file is limited to 64K bytes. This function serializes the XMP + /// metadata in an XMP object into a string of RDF (see \c TXMPMeta::SerializeToBuffer()). If + /// the data does not fit into the 64K byte limit, it creates a second packet string with the + /// extended data. + /// + /// @param xmpObj The XMP object containing the metadata. + /// + /// @param standardXMP [out] A string object in which to return the full standard XMP packet. + /// + /// @param extendedXMP [out] A string object in which to return the serialized extended XMP, + /// empty if not needed. + /// + /// @param extendedDigest [out] A string object in which to return an MD5 digest of the serialized + /// extended XMP, empty if not needed. + /// + /// @see \c MergeFromJPEG() + + static void PackageForJPEG ( const TXMPMeta & xmpObj, + tStringObj * standardXMP, + tStringObj * extendedXMP, + tStringObj * extendedDigest ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c MergeFromJPEG() merges standard and extended XMP retrieved from a JPEG file. + /// + /// When an extended partition stores properties that do not fit into the JPEG file limitation + /// of 64K bytes, this function integrates those properties back into the same XMP object with + /// those from the standard XMP packet. + /// + /// @param fullXMP [in, out] An XMP object which the caller has initialized from the standard + /// XMP packet in a JPEG file. The extended XMP is added to this object. + /// + /// @param extendedXMP An XMP object which the caller has initialized from the extended XMP + /// packet in a JPEG file. + /// + /// @see \c PackageForJPEG() + + static void MergeFromJPEG ( TXMPMeta * fullXMP, + const TXMPMeta & extendedXMP ); + + /// @} + + // ============================================================================================= + /// \name Editing utilities + /// @{ + /// + /// These functions are useful in implementing a user interface for editing XMP. They + /// convert sets of property values to and from displayable and manipulable strings, and perform + /// operations on sets of metadata, such as those available from the File Info dialog box. + + // --------------------------------------------------------------------------------------------- + /// @brief \c CatenateArrayItems() creates a single edit string from a set of array item values. + /// + /// Collects the values of all items in an array into a single string, using a specified + /// separation string. Each item in the specified array must be a simple string value. + /// + /// @param xmpObj The XMP object containing the array to be catenated. + /// + /// @param schemaNS The schema namespace URI for the array. Must not be null or the empty string. + /// + /// @param arrayName The name of the array. May be a general path expression, must not be null + /// or the empty string. + /// + /// @param separator The string with which to separate the items in the catenated string. + /// Defaults to "; ", ASCII semicolon and space (U+003B, U+0020). + /// + /// @param quotes The character or characters to use as quotes around array items that contain a + /// separator. Defaults to the double-quote character ("), ASCII quote (U+0022). + /// + /// @param options Option flags to control the catenation. <> + /// + /// @param catedStr [out] A string object in which to return the catenated array items. + /// + /// @see \c SeparateArrayItems() + + static void CatenateArrayItems ( const TXMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr separator, + XMP_StringPtr quotes, + XMP_OptionBits options, + tStringObj * catedStr ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SeparateArrayItems() updates an array from a concatenated edit string of values. + /// + /// This reverses the action of \c CatenateArrayItems(), separating out individual array items + /// from the edit string and updating the array with the new values. Each item in the array must + /// be a simple string value. + /// + /// @param xmpObj The XMP object containing the array to be updated. + /// + /// @param schemaNS The schema namespace URI for the array. Must not be null or the empty string. + /// + /// @param arrayName The name of the array. May be a general path expression, must not be null + /// or the empty string. + /// + /// @param options Option flags to control the separation. <> + /// + /// @param catedStr The concatenated array items, as created by \c CatenateArrayItems(), + /// specified as a null-terminated UTF-8 string. + + static void SeparateArrayItems ( TXMPMeta * xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + XMP_StringPtr catedStr ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c SeparateArrayItems() updates an array from a concatenated edit string of values. + /// + /// Overloads the basic form of the function, allowing you to pass a string object in which + /// to return the concatenated string. It is otherwise identical; see details for the canonical form. + /// + + static void SeparateArrayItems ( TXMPMeta * xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + const tStringObj & catedStr ); + + /// @brief \c ApplyTemplate() modifies a working XMP object according to a template object. + /// + /// The XMP template can be used to add, replace or delete properties from the working XMP object. + /// This function replaces the previous \c AppendProperties() function, which is no longer available. + /// The actions that you specify determine how the template is applied. Each action can be applied + /// individually or combined; if you do not specify any actions, the properties and values in the working + /// XMP object do not change. + /// + /// These actions are available: + /// \li Clear (\c #kXMPTemplate_ClearUnnamedProperties): Deletes top-level properties. + /// Any top-level property that is present in the template (even with empty value) + /// is retained. All other top-level properties in the working object are deleted. + /// + /// \li Add (\c #kXMPTemplate_AddNewProperties): Adds new properties to the working object if the + /// template properties have values. See additional detail below. + /// + /// \li Replace (\c #kXMPTemplate_ReplaceExistingProperties): Replaces the values of existing top-level + /// properties in the working XMP if the value forms match those in the template. Properties + /// with empty values in the template are ignored. If combined with Clear or Add actions, + /// those take precedence; values are cleared or added, rather than replaced. + /// + /// \li Replace/Delete empty (\c #kXMPTemplate_ReplaceWithDeleteEmpty): Replaces values in the same way + /// as the simple Replace action, and also deletes properties if the value in the template is empty. + /// If combined with Clear or Add actions, those take precedence; values are cleared or added, + /// rather than replaced. + /// + /// \li Include internal (\c #kXMPTemplate_IncludeInternalProperties): Performs specified action + /// on internal properties as well as external properties. By default, internal properties + /// are ignored for all actions. + /// + /// The Add behavior depends on the type of property: + ///
    + ///
  • If a top-level property is not in the working XMP, and has a value in the template, + /// the property and value are added. Empty properties are not added.
  • + ///
  • If a property is in both the working XMP and template, the value forms must match, otherwise + /// the template is ignored for that property.
  • + ///
  • If a struct is present in both the working XMP and template, the individual fields of the + /// template struct are added as appropriate; that is, the logic is recursively applied to the fields. + /// Struct values are equivalent if they have the same fields with equivalent values.
  • + ///
  • If an array is present in both the working XMP and template, items from the template are + /// added if the value forms match. Array values match if they have sets of equivalent items, + /// regardless of order.
  • + ///
  • Alt-text arrays use the \c xml:lang qualifier as a key, adding languages that are missing.
  • + ///
+ /// Array item checking is n-squared; this can be time-intensive if the Replace option is + /// not specified. Each source item is checked to see if it already exists in the destination, + /// without regard to order or duplicates. Simple items are compared by value and \c xml:lang + /// qualifier; other qualifiers are ignored. Structs are recursively compared by field names, + /// without regard to field order. Arrays are compared by recursively comparing all items. + + /// @param workingXMP The destination XMP object. + /// + /// @param templateXMP The template to apply to the destination XMP object. + /// + /// @param actions Option flags to control the copying. If none are specified, the properties and values + /// in the working XMP do not change. A logical OR of these bit-flag constants: + /// \li \c #kXMPTemplate_ClearUnnamedProperties -- Delete anything that is not in the template + /// \li \c #kXMPTemplate_AddNewProperties -- Add properties; see detailed description. + /// \li \c #kXMPTemplate_ReplaceExistingProperties -- Replace the values of existing properties. + /// \li \c #kXMPTemplate_ReplaceWithDeleteEmpty -- Replace the values of existing properties + /// and delete properties if the new value is empty. + /// \li \c #kXMPTemplate_IncludeInternalProperties -- Operate on internal properties as well as + /// external properties. + /// + + static void ApplyTemplate ( TXMPMeta * workingXMP, + const TXMPMeta & templateXMP, + XMP_OptionBits actions ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c RemoveProperties() removes multiple properties from an XMP object. + /// + /// The operation depends on how the namespace and property are specified: + /// + /// \li Non-empty \c schemaNS and \c propName - The named property is removed if it is an + /// external property, or if the \c #kXMPUtil_DoAllProperties option flag is set. It does not + /// matter whether the named property is an actual property or an alias. + /// + /// \li Non-empty \c schemaNS and empty \c propName - All external properties in the named + /// schema are removed. Internal properties are also removed if the + /// \c #kXMPUtil_DoAllProperties option flag is set. In addition, aliases from the named schema + /// are removed if the \c #kXMPUtil_IncludeAliases option flag is set. + /// + /// \li Empty \c schemaNS and empty \c propName - All external properties in all schemas are + /// removed. Internal properties are also removed if the \c #kXMPUtil_DoAllProperties option + /// flag is set. Aliases are handled implicitly, because the associated actuals are removed or + /// not. + /// + /// \li It is an error to pass an empty \c schemaNS and non-empty \c propName. + /// + /// @param xmpObj The XMP object containing the properties to be removed. + /// + /// @param schemaNS Optional schema namespace URI for the properties to be removed. + /// + /// @param propName Optional path expression for the property to be removed. + /// + /// @param options Option flags to control the deletion operation. A logical OR of these + /// bit-flag constants: + /// \li \c #kXMPUtil_DoAllProperties - Delete internal properties in addition to external properties. + /// \li \c #kXMPUtil_IncludeAliases - Include aliases if the schema is explicitly specified. + + static void RemoveProperties ( TXMPMeta * xmpObj, + XMP_StringPtr schemaNS = 0, + XMP_StringPtr propName = 0, + XMP_OptionBits options = 0 ); + + // --------------------------------------------------------------------------------------------- + /// @brief \c DuplicateSubtree() replicates a subtree from one XMP object into another. + /// + /// The destination can be a different namespace and root location in the same object, or the + /// same or a different location in another XMP object. + /// + /// @param source The source XMP object. + /// + /// @param dest The destination XMP object. + /// + /// @param sourceNS The schema namespace URI for the source subtree. + /// + /// @param sourceRoot The root location for the source subtree. Can be a general path expression, + /// must not be null or the empty string. + /// + /// @param destNS The schema namespace URI for the destination. Defaults to the source namespace. + /// + /// @param destRoot The root location for the destination. Can be a general path expression. + /// Defaults to the source location. + /// + /// @param options Option flags to control the operation. <> + + static void DuplicateSubtree ( const TXMPMeta & source, + TXMPMeta * dest, + XMP_StringPtr sourceNS, + XMP_StringPtr sourceRoot, + XMP_StringPtr destNS = 0, + XMP_StringPtr destRoot = 0, + XMP_OptionBits options = 0 ); + + /// @} + + // ============================================================================================= + +private: + + static void SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ); + +}; // class TXMPUtils + +// ================================================================================================= + +#endif // __TXMPUtils_hpp__ diff --git a/source/lib/xmp_core/public/include/XMP.hpp b/source/lib/xmp_core/public/include/XMP.hpp new file mode 100644 index 0000000..8ec39eb --- /dev/null +++ b/source/lib/xmp_core/public/include/XMP.hpp @@ -0,0 +1,98 @@ +#ifndef __XMP_hpp__ +#define __XMP_hpp__ 1 + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================ +/// \file XMP.hpp +/// \brief Overall header file for the XMP Toolkit +/// +/// This is an overall header file, the only one that C++ clients should include. +/// +/// The full client API is in the \c TXMPMeta.hpp, \c TXMPIterator.hpp, \c TXMPUtils.hpp headers. +/// Read these for information, but do not include them directly. The \c TXMP... classes are C++ +/// template classes that must be instantiated with a string class such as \c std::string. The +/// string class is used to return text strings for property values, serialized XMP, and so on. +/// Clients must also compile \c XMP.incl_cpp to ensure that all client-side glue code is generated. +/// This should be done by including it in exactly one client source file. +/// +/// There are two C preprocessor macros that simplify use of the templates: +/// +/// \li \c TXMP_STRING_TYPE - Define this as the string class to use with the template. You will get +/// the template headers included and typedefs (\c SXMPMeta, and so on) to use in your code. +/// +/// \li \c TXMP_EXPAND_INLINE - Define this as 1 if you want to have the template functions expanded +/// inline in your code. Leave it undefined, or defined as 0, to use out-of-line instantiations of +/// the template functions. Compiling \c XMP.incl_cpp generates explicit out-of-line +/// instantiations if \c TXMP_EXPAND_INLINE is off. +/// +/// The template parameter, class \c tStringObj, must have the following member functions (which +/// match those for \c std::string): +/// +///
+///  tStringObj& assign ( const char * str, size_t len )
+///  size_t size() const
+///  const char * c_str() const
+/// 
+/// +/// The string class must be suitable for at least UTF-8. This is the encoding used for all general +/// values, and is the default encoding for serialized XMP. The string type must also be suitable +/// for UTF-16 or UTF-32 if those serialization encodings are used. This mainly means tolerating +/// embedded 0 bytes, which \c std::string does. +// ================================================================================================ + +/// /c XMP_Environment.h must be the first included header. +#include "XMP_Environment.h" + +#include "XMP_Version.h" +#include "XMP_Const.h" + +#if XMP_WinBuild + #if XMP_DebugBuild + #pragma warning ( push, 4 ) + #else + #pragma warning ( push, 3 ) + #endif + #pragma warning ( disable : 4702 ) // unreachable code + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) +#endif + +#if defined ( TXMP_STRING_TYPE ) + + #include "TXMPMeta.hpp" + #include "TXMPIterator.hpp" + #include "TXMPUtils.hpp" + typedef class TXMPMeta SXMPMeta; // For client convenience. + typedef class TXMPIterator SXMPIterator; + typedef class TXMPUtils SXMPUtils; + #if TXMP_EXPAND_INLINE + #error "TXMP_EXPAND_INLINE is not working at present. Please don't use it." + #include "client-glue/TXMPMeta.incl_cpp" + #include "client-glue/TXMPIterator.incl_cpp" + #include "client-glue/TXMPUtils.incl_cpp" + #include "client-glue/TXMPFiles.incl_cpp" + #endif + + #if XMP_INCLUDE_XMPFILES + #include "TXMPFiles.hpp" // ! Needs typedef for SXMPMeta. + typedef class TXMPFiles SXMPFiles; + #if TXMP_EXPAND_INLINE + #include "client-glue/TXMPFiles.incl_cpp" + #endif + #endif + +#endif // TXMP_STRING_TYPE + +#if XMP_WinBuild + #pragma warning ( pop ) +#endif + +// ================================================================================================= + +#endif // __XMP_hpp__ diff --git a/source/lib/xmp_core/public/include/XMP.incl_cpp b/source/lib/xmp_core/public/include/XMP.incl_cpp new file mode 100644 index 0000000..fe2a6fa --- /dev/null +++ b/source/lib/xmp_core/public/include/XMP.incl_cpp @@ -0,0 +1,69 @@ +#ifndef __XMP_incl_cpp__ +#define __XMP_incl_cpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================ +/// \file XMP.incl_cpp +/// \brief Overall client glue file for the XMP toolkit. +/// +/// This is an overall client source file of XMP toolkit glue, the only XMP-specific one that +/// clients should build in projects. This ensures that all of the client-side glue code for the +/// XMP toolkit gets compiled. +/// +/// You cannot compile this file directly, because the template's string type must be declared and +/// only the client can do that. Instead, include this in some other source file. For example, +/// to use std::string you only need these two lines: +/// +/// \code +/// #include +/// #include "XMP.incl_cpp" +/// \endcode + + +#include "XMP.hpp" // ! This must be the first include! + +#define XMP_ClientBuild 1 + +#if XMP_WinBuild + #if XMP_DebugBuild + #pragma warning ( push, 4 ) + #else + #pragma warning ( push, 3 ) + #endif + + #pragma warning ( disable : 4127 ) // conditional expression is constant + #pragma warning ( disable : 4189 ) // local variable is initialized but not referenced + #pragma warning ( disable : 4702 ) // unreachable code + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) +#endif + +#if defined ( TXMP_STRING_TYPE ) && (! TXMP_EXPAND_INLINE) + + // We're using a single out of line instantiation. Do it here. + + #include "client-glue/TXMPMeta.incl_cpp" + #include "client-glue/TXMPIterator.incl_cpp" + #include "client-glue/TXMPUtils.incl_cpp" + template class TXMPMeta ; + template class TXMPIterator ; + template class TXMPUtils ; + #if XMP_INCLUDE_XMPFILES + #include "client-glue/TXMPFiles.incl_cpp" + template class TXMPFiles ; + #endif + +#endif + +#if XMP_WinBuild + #pragma warning ( pop ) +#endif + +#endif // __XMP_incl_cpp__ diff --git a/source/lib/xmp_core/public/include/XMP_Const.h b/source/lib/xmp_core/public/include/XMP_Const.h new file mode 100644 index 0000000..eadc267 --- /dev/null +++ b/source/lib/xmp_core/public/include/XMP_Const.h @@ -0,0 +1,1560 @@ +#ifndef __XMP_Const_h__ +#define __XMP_Const_h__ 1 + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMP_Environment.h" + + #include + +#if XMP_MacBuild | XMP_iOSBuild // ! No stdint.h on Windows and some UNIXes. + #include +#endif +#if XMP_UNIXBuild // hopefully an inttypes.h on all UNIXes... + #include +#endif + + +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= +/// \file XMP_Const.h +/// \brief Common C/C++ types and constants for the XMP toolkit. +// ================================================================================================= + +// ================================================================================================= +// Basic types and constants +// ========================= + +// The XMP_... types are used on the off chance that the ..._t types present a problem. In that +// case only the declarations of the XMP_... types needs to change, not all of the uses. These +// types are used where fixed sizes are required in order to have a known ABI for a DLL build. + +#if XMP_MacBuild | XMP_iOSBuild + + typedef int8_t XMP_Int8; + typedef int16_t XMP_Int16; + typedef int32_t XMP_Int32; + typedef int64_t XMP_Int64; + + typedef uint8_t XMP_Uns8; + typedef uint16_t XMP_Uns16; + typedef uint32_t XMP_Uns32; + typedef uint64_t XMP_Uns64; + +#elif XMP_WinBuild + + typedef signed char XMP_Int8; + typedef signed short XMP_Int16; + typedef signed long XMP_Int32; + typedef signed long long XMP_Int64; + + typedef unsigned char XMP_Uns8; + typedef unsigned short XMP_Uns16; + typedef unsigned long XMP_Uns32; + typedef unsigned long long XMP_Uns64; + +#elif XMP_UNIXBuild + + #if ! XMP_64 + + typedef signed char XMP_Int8; + typedef signed short XMP_Int16; + typedef signed long XMP_Int32; + typedef signed long long XMP_Int64; + + typedef unsigned char XMP_Uns8; + typedef unsigned short XMP_Uns16; + typedef unsigned long XMP_Uns32; + typedef unsigned long long XMP_Uns64; + + #else + + typedef signed char XMP_Int8; + typedef signed short XMP_Int16; + typedef signed int XMP_Int32; + typedef signed long long XMP_Int64; + + typedef unsigned char XMP_Uns8; + typedef unsigned short XMP_Uns16; + typedef unsigned int XMP_Uns32; + typedef unsigned long long XMP_Uns64; + + #endif + +#else + + #error "XMP environment error - must define one of XMP_MacBuild, XMP_WinBuild, XMP_UNIXBuild or XMP_iOSBuild" + +#endif + +typedef XMP_Uns8 XMP_Bool; + +const XMP_Uns8 kXMP_Bool_False = 0; + +#define ConvertXMP_BoolToBool(a) (a) != kXMP_Bool_False +#define ConvertBoolToXMP_Bool(a) (a) ? !kXMP_Bool_False : kXMP_Bool_False + +static const XMP_Uns8 Min_XMP_Uns8 = ( (XMP_Uns8) 0x00 ); +static const XMP_Uns8 Max_XMP_Uns8 = ( (XMP_Uns8) 0xFF ); +static const XMP_Uns16 Min_XMP_Uns16 = ( (XMP_Uns16) 0x00 ); +static const XMP_Uns16 Max_XMP_Uns16 = ( (XMP_Uns16) 0xFFFF ); +static const XMP_Uns32 Min_XMP_Uns32 = ( (XMP_Uns32) 0x00 ); +static const XMP_Uns32 Max_XMP_Uns32 = ( (XMP_Uns32) 0xFFFFFFFF ); +static const XMP_Uns64 Min_XMP_Uns64 = ( (XMP_Uns64) 0x00 ); +static const XMP_Uns64 Max_XMP_Uns64 = ( (XMP_Uns64) 0xFFFFFFFFFFFFFFFFLL ); + +static const XMP_Int8 Min_XMP_Int8 = ( (XMP_Int8) 0x80 ); +static const XMP_Int8 Max_XMP_Int8 = ( (XMP_Int8) 0x7F ); +static const XMP_Int16 Min_XMP_Int16 = ( (XMP_Int16) 0x8000 ); +static const XMP_Int16 Max_XMP_Int16 = ( (XMP_Int16) 0x7FFF ); +static const XMP_Int32 Min_XMP_Int32 = ( (XMP_Int32) 0x80000000 ); +static const XMP_Int32 Max_XMP_Int32 = ( (XMP_Int32) 0x7FFFFFFF ); +static const XMP_Int64 Min_XMP_Int64 = ( (XMP_Int64) 0x8000000000000000LL ); +static const XMP_Int64 Max_XMP_Int64 = ( (XMP_Int64) 0x7FFFFFFFFFFFFFFFLL ); + + +/// An "ABI safe" pointer to the internal part of an XMP object. Use to pass an XMP object across +/// client DLL boundaries. See \c TXMPMeta::GetInternalRef(). +typedef struct __XMPMeta__ * XMPMetaRef; + +/// An "ABI safe" pointer to the internal part of an XMP iteration object. Use to pass an XMP +/// iteration object across client DLL boundaries. See \c TXMPIterator. +typedef struct __XMPIterator__ * XMPIteratorRef; + +/// An "ABI safe" pointer to the internal part of an XMP document operations object. Use to pass an +/// XMP document operations object across client DLL boundaries. See \c TXMPDocOps. +typedef struct __XMPDocOps__ * XMPDocOpsRef; + +/// An "ABI safe" pointer to the internal part of an XMP file-handling object. Use to pass an XMP +/// file-handling object across client DLL boundaries. See \c TXMPFiles. +typedef struct __XMPFiles__ * XMPFilesRef; + +// ================================================================================================= + +/// \name General scalar types and constants +/// @{ + +/// \typedef XMP_StringPtr +/// \brief The type for input string parameters. A const char *, a null-terminated UTF-8 +/// string. + +/// \typedef XMP_StringLen +/// \brief The type for string length parameters. A 32-bit unsigned integer, as big as will be +/// practically needed. + +/// \typedef XMP_Index +/// \brief The type for offsets and indices. A 32-bit signed integer. It is signed to allow -1 for +/// loop termination. + +/// \typedef XMP_OptionBits +/// \brief The type for a collection of 32 flag bits. Individual flags are defined as enum value bit +/// masks; see \c #kXMP_PropValueIsURI and following. A number of macros provide common set or set +/// operations, such as \c XMP_PropIsSimple. For other tests use an expression like options & +/// kXMP_. When passing multiple option flags use the bitwise-OR operator. '|', +/// not the arithmatic plus, '+'. + +typedef const char * XMP_StringPtr; // Points to a null terminated UTF-8 string. +typedef XMP_Uns32 XMP_StringLen; +typedef XMP_Int32 XMP_Index; // Signed, sometimes -1 is handy. +typedef XMP_Uns32 XMP_OptionBits; // Used as 32 individual bits. + +/// \def kXMP_TrueStr +/// \brief The canonical true string value for Booleans in serialized XMP. +/// +/// Code that converts from string to bool should be case insensitive, and also allow "1". + +/// \def kXMP_FalseStr +/// \brief The canonical false string value for Booleans in serialized XMP. +/// +/// Code that converts from string to bool should be case insensitive, and also allow "0". + +#define kXMP_TrueStr "True" // Serialized XMP spellings, not for the type bool. +#define kXMP_FalseStr "False" + +/// Type for yes/no/maybe answers. The values are picked to allow Boolean-like usage. The yes and +/// values are true (non-zero), the no value is false (zero). +enum { + /// The part or parts have definitely changed. + kXMPTS_Yes = 1, + /// The part or parts have definitely not changed. + kXMPTS_No = 0, + /// The part or parts might, or might not, have changed. + kXMPTS_Maybe = -1 +}; +typedef XMP_Int8 XMP_TriState; + +/// @} + +// ================================================================================================= + +/// \struct XMP_DateTime +/// \brief The expanded type for a date and time. +/// +/// Dates and time in the serialized XMP are ISO 8601 strings. The \c XMP_DateTime struct allows +/// easy conversion with other formats. +/// +/// All of the fields are 32 bit, even though most could be 8 bit. This avoids overflow when doing +/// carries for arithmetic or normalization. All fields have signed values for the same reasons. +/// +/// Date-time values are occasionally used with only a date or only a time component. A date without +/// a time has zeros in the \c XMP_DateTime struct for all time fields. A time without a date has +/// zeros for all date fields (year, month, and day). +/// +/// \c TXMPUtils provides utility functions for manipulating date-time values. +/// +/// @see \c TXMPUtils::ConvertToDate(), \c TXMPUtils::ConvertFromDate(), +/// \c TXMPUtils::CompareDateTime(), \c TXMPUtils::ConvertToLocalTime(), +/// \c TXMPUtils::ConvertToUTCTime(), \c TXMPUtils::CurrentDateTime(), +/// \c TXMPUtils::SetTimeZone() + +struct XMP_DateTime { + + /// The year, can be negative. + XMP_Int32 year; + + /// The month in the range 1..12. + XMP_Int32 month; + + /// The day of the month in the range 1..31. + XMP_Int32 day; + + /// The hour in the range 0..23. + XMP_Int32 hour; + + /// The minute in the range 0..59. + XMP_Int32 minute; + + /// The second in the range 0..59. + XMP_Int32 second; + + /// Is the date portion meaningful? + XMP_Bool hasDate; + + /// Is the time portion meaningful? + XMP_Bool hasTime; + + /// Is the time zone meaningful? + XMP_Bool hasTimeZone; + + /// The "sign" of the time zone, \c #kXMP_TimeIsUTC (0) means UTC, \c #kXMP_TimeWestOfUTC (-1) + /// is west, \c #kXMP_TimeEastOfUTC (+1) is east. + XMP_Int8 tzSign; + + /// The time zone hour in the range 0..23. + XMP_Int32 tzHour; + + /// The time zone minute in the range 0..59. + XMP_Int32 tzMinute; + + /// Nanoseconds within a second, often left as zero. + XMP_Int32 nanoSecond; + + #if __cplusplus + XMP_DateTime() : year(0), month(0), day(0), hour(0), minute(0), second(0), + hasDate(false),hasTime(false), hasTimeZone(false), tzSign(0), tzHour(0), tzMinute(0), nanoSecond(0){}; + #endif + +}; + +/// Constant values for \c XMP_DateTime::tzSign field. +enum { + /// Time zone is west of UTC. + kXMP_TimeWestOfUTC = -1, + /// UTC time. + kXMP_TimeIsUTC = 0, + /// Time zone is east of UTC. + kXMP_TimeEastOfUTC = +1 +}; + +#define XMPDateTime_IsDateOnly(dt) ((dt).hasDate & (! (dt).hasTime)) +#define XMPDateTime_IsTimeOnly(dt) ((dt).hasTime & (! (dt).hasDate)) + +#define XMPDateTime_ClearTimeZone(dt) { (dt).hasTimeZone = (dt).tzSign = (dt).tzHour = (dt).tzMinute = 0; } + +// ================================================================================================= +// Standard namespace URI constants +// ================================ + +/// \name XML namespace constants for standard XMP schema. +/// @{ +/// +/// \def kXMP_NS_XMP +/// \brief The XML namespace for the XMP "basic" schema. +/// +/// \def kXMP_NS_XMP_Rights +/// \brief The XML namespace for the XMP copyright schema. +/// +/// \def kXMP_NS_XMP_MM +/// \brief The XML namespace for the XMP digital asset management schema. +/// +/// \def kXMP_NS_XMP_BJ +/// \brief The XML namespace for the job management schema. +/// +/// \def kXMP_NS_XMP_T +/// \brief The XML namespace for the XMP text document schema. +/// +/// \def kXMP_NS_XMP_T_PG +/// \brief The XML namespace for the XMP paged document schema. +/// +/// \def kXMP_NS_PDF +/// \brief The XML namespace for the PDF schema. +/// +/// \def kXMP_NS_Photoshop +/// \brief The XML namespace for the Photoshop custom schema. +/// +/// \def kXMP_NS_EXIF +/// \brief The XML namespace for Adobe's EXIF schema. +/// +/// \def kXMP_NS_TIFF +/// \brief The XML namespace for Adobe's TIFF schema. +/// +/// @} + +#define kXMP_NS_XMP "http://ns.adobe.com/xap/1.0/" + +#define kXMP_NS_XMP_Rights "http://ns.adobe.com/xap/1.0/rights/" +#define kXMP_NS_XMP_MM "http://ns.adobe.com/xap/1.0/mm/" +#define kXMP_NS_XMP_BJ "http://ns.adobe.com/xap/1.0/bj/" + +#define kXMP_NS_PDF "http://ns.adobe.com/pdf/1.3/" +#define kXMP_NS_Photoshop "http://ns.adobe.com/photoshop/1.0/" +#define kXMP_NS_PSAlbum "http://ns.adobe.com/album/1.0/" +#define kXMP_NS_EXIF "http://ns.adobe.com/exif/1.0/" +#define kXMP_NS_EXIF_Aux "http://ns.adobe.com/exif/1.0/aux/" +#define kXMP_NS_TIFF "http://ns.adobe.com/tiff/1.0/" +#define kXMP_NS_PNG "http://ns.adobe.com/png/1.0/" +#define kXMP_NS_SWF "http://ns.adobe.com/swf/1.0/" +#define kXMP_NS_JPEG "http://ns.adobe.com/jpeg/1.0/" +#define kXMP_NS_JP2K "http://ns.adobe.com/jp2k/1.0/" +#define kXMP_NS_CameraRaw "http://ns.adobe.com/camera-raw-settings/1.0/" +#define kXMP_NS_DM "http://ns.adobe.com/xmp/1.0/DynamicMedia/" +#define kXMP_NS_Script "http://ns.adobe.com/xmp/1.0/Script/" +#define kXMP_NS_ASF "http://ns.adobe.com/asf/1.0/" +#define kXMP_NS_WAV "http://ns.adobe.com/xmp/wav/1.0/" +#define kXMP_NS_BWF "http://ns.adobe.com/bwf/bext/1.0/" +#define kXMP_NS_AEScart "http://ns.adobe.com/aes/cart/" +#define kXMP_NS_RIFFINFO "http://ns.adobe.com/riff/info/" + +#define kXMP_NS_XMP_Note "http://ns.adobe.com/xmp/note/" + +#define kXMP_NS_AdobeStockPhoto "http://ns.adobe.com/StockPhoto/1.0/" +#define kXMP_NS_CreatorAtom "http://ns.adobe.com/creatorAtom/1.0/" + +#define kXMP_NS_ExifEX "http://cipa.jp/exif/1.0/" + +/// \name XML namespace constants for qualifiers and structured property fields. +/// @{ +/// +/// \def kXMP_NS_XMP_IdentifierQual +/// \brief The XML namespace for qualifiers of the xmp:Identifier property. +/// +/// \def kXMP_NS_XMP_Dimensions +/// \brief The XML namespace for fields of the Dimensions type. +/// +/// \def kXMP_NS_XMP_Image +/// \brief The XML namespace for fields of a graphical image. Used for the Thumbnail type. +/// +/// \def kXMP_NS_XMP_ResourceEvent +/// \brief The XML namespace for fields of the ResourceEvent type. +/// +/// \def kXMP_NS_XMP_ResourceRef +/// \brief The XML namespace for fields of the ResourceRef type. +/// +/// \def kXMP_NS_XMP_ST_Version +/// \brief The XML namespace for fields of the Version type. +/// +/// \def kXMP_NS_XMP_ST_Job +/// \brief The XML namespace for fields of the JobRef type. +/// +/// @} + +#define kXMP_NS_XMP_IdentifierQual "http://ns.adobe.com/xmp/Identifier/qual/1.0/" +#define kXMP_NS_XMP_Dimensions "http://ns.adobe.com/xap/1.0/sType/Dimensions#" +#define kXMP_NS_XMP_Text "http://ns.adobe.com/xap/1.0/t/" +#define kXMP_NS_XMP_PagedFile "http://ns.adobe.com/xap/1.0/t/pg/" +#define kXMP_NS_XMP_Graphics "http://ns.adobe.com/xap/1.0/g/" +#define kXMP_NS_XMP_Image "http://ns.adobe.com/xap/1.0/g/img/" +#define kXMP_NS_XMP_Font "http://ns.adobe.com/xap/1.0/sType/Font#" +#define kXMP_NS_XMP_ResourceEvent "http://ns.adobe.com/xap/1.0/sType/ResourceEvent#" +#define kXMP_NS_XMP_ResourceRef "http://ns.adobe.com/xap/1.0/sType/ResourceRef#" +#define kXMP_NS_XMP_ST_Version "http://ns.adobe.com/xap/1.0/sType/Version#" +#define kXMP_NS_XMP_ST_Job "http://ns.adobe.com/xap/1.0/sType/Job#" +#define kXMP_NS_XMP_ManifestItem "http://ns.adobe.com/xap/1.0/sType/ManifestItem#" + +// Deprecated XML namespace constants +#define kXMP_NS_XMP_T "http://ns.adobe.com/xap/1.0/t/" +#define kXMP_NS_XMP_T_PG "http://ns.adobe.com/xap/1.0/t/pg/" +#define kXMP_NS_XMP_G_IMG "http://ns.adobe.com/xap/1.0/g/img/" + +/// \name XML namespace constants from outside Adobe. +/// @{ +/// +/// \def kXMP_NS_DC +/// \brief The XML namespace for the Dublin Core schema. +/// +/// \def kXMP_NS_IPTCCore +/// \brief The XML namespace for the IPTC Core schema. +/// +/// \def kXMP_NS_IPTCExt +/// \brief The XML namespace for the IPTC Extension schema. +/// +/// \def kXMP_NS_RDF +/// \brief The XML namespace for RDF. +/// +/// \def kXMP_NS_XML +/// \brief The XML namespace for XML. +/// +/// @} + +#define kXMP_NS_DC "http://purl.org/dc/elements/1.1/" + +#define kXMP_NS_IPTCCore "http://iptc.org/std/Iptc4xmpCore/1.0/xmlns/" +#define kXMP_NS_IPTCExt "http://iptc.org/std/Iptc4xmpExt/2008-02-29/" + +#define kXMP_NS_DICOM "http://ns.adobe.com/DICOM/" + +#define kXMP_NS_PLUS "http://ns.useplus.org/ldf/xmp/1.0/" + +#define kXMP_NS_PDFA_Schema "http://www.aiim.org/pdfa/ns/schema#" +#define kXMP_NS_PDFA_Property "http://www.aiim.org/pdfa/ns/property#" +#define kXMP_NS_PDFA_Type "http://www.aiim.org/pdfa/ns/type#" +#define kXMP_NS_PDFA_Field "http://www.aiim.org/pdfa/ns/field#" +#define kXMP_NS_PDFA_ID "http://www.aiim.org/pdfa/ns/id/" +#define kXMP_NS_PDFA_Extension "http://www.aiim.org/pdfa/ns/extension/" + +#define kXMP_NS_PDFX "http://ns.adobe.com/pdfx/1.3/" +#define kXMP_NS_PDFX_ID "http://www.npes.org/pdfx/ns/id/" + +#define kXMP_NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" +#define kXMP_NS_XML "http://www.w3.org/XML/1998/namespace" + +// ================================================================================================= +// Enums and macros used for option bits +// ===================================== + +/// \name Macros for standard option selections. +/// @{ +/// +/// \def kXMP_ArrayLastItem +/// \brief Options macro accesses last array item. +/// +/// \def kXMP_UseNullTermination +/// \brief Options macro sets string style. +/// +/// \def kXMP_NoOptions +/// \brief Options macro clears all property-type bits. +/// +/// @} + +#define kXMP_ArrayLastItem ((XMP_Index)(-1L)) +#define kXMP_UseNullTermination ((XMP_StringLen)(~0UL)) +#define kXMP_NoOptions ((XMP_OptionBits)0UL) + +/// \name Macros for setting and testing general option bits. +/// @{ +/// +/// \def XMP_SetOption +/// \brief Macro sets an option flag bit. +/// \param var A variable storing an options flag. +/// \param opt The bit-flag constant to set. +/// +/// \def XMP_ClearOption +/// \brief Macro clears an option flag bit. +/// \param var A variable storing an options flag. +/// \param opt The bit-flag constant to clear. +/// +/// \def XMP_TestOption +/// \brief Macro reports whether an option flag bit is set. +/// \param var A variable storing an options flag. +/// \param opt The bit-flag constant to test. +/// \return True if the bit is set. +/// +/// \def XMP_OptionIsSet +/// \brief Macro reports whether an option flag bit is set. +/// \param var A variable storing an options flag. +/// \param opt The bit-flag constant to test. +/// \return True if the bit is set. +/// +/// \def XMP_OptionIsClear +/// \brief Macro reports whether an option flag bit is clear. +/// \param var A variable storing an options flag. +/// \param opt The bit-flag constant to test. +/// \return True if the bit is clear. +/// +/// @} + +#define XMP_SetOption(var,opt) var |= (opt) +#define XMP_ClearOption(var,opt) var &= ~(opt) +#define XMP_TestOption(var,opt) (((var) & (opt)) != 0) +#define XMP_OptionIsSet(var,opt) (((var) & (opt)) != 0) +#define XMP_OptionIsClear(var,opt) (((var) & (opt)) == 0) + +/// \name Macros for setting and testing specific option bits. +/// @{ +/// +/// \def XMP_PropIsSimple +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_PropIsStruct +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_PropIsArray +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_ArrayIsUnordered +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_ArrayIsOrdered +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_ArrayIsAlternate +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_ArrayIsAltText +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_PropHasQualifiers +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_PropIsQualifier +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_PropHasLang +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_NodeIsSchema +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// \def XMP_PropIsAlias +/// \brief Macro reports the property type specified by an options flag. +/// \param opt The options flag to check. +/// +/// @} + +#define XMP_PropIsSimple(opt) (((opt) & kXMP_PropCompositeMask) == 0) +#define XMP_PropIsStruct(opt) (((opt) & kXMP_PropValueIsStruct) != 0) +#define XMP_PropIsArray(opt) (((opt) & kXMP_PropValueIsArray) != 0) + +#define XMP_ArrayIsUnordered(opt) (((opt) & kXMP_PropArrayIsOrdered) == 0) +#define XMP_ArrayIsOrdered(opt) (((opt) & kXMP_PropArrayIsOrdered) != 0) +#define XMP_ArrayIsAlternate(opt) (((opt) & kXMP_PropArrayIsAlternate) != 0) +#define XMP_ArrayIsAltText(opt) (((opt) & kXMP_PropArrayIsAltText) != 0) + +#define XMP_PropHasQualifiers(opt) (((opt) & kXMP_PropHasQualifiers) != 0) +#define XMP_PropIsQualifier(opt) (((opt) & kXMP_PropIsQualifier) != 0) +#define XMP_PropHasLang(opt) (((opt) & kXMP_PropHasLang) != 0) + +#define XMP_NodeIsSchema(opt) (((opt) & kXMP_SchemaNode) != 0) +#define XMP_PropIsAlias(opt) (((opt) & kXMP_PropIsAlias) != 0) + +// ------------------------------------------------------------------------------------------------- + +/// Option bit flags for the \c TXMPMeta property accessor functions. +enum { + + /// The XML string form of the property value is a URI, use rdf:resource attribute. DISCOURAGED + kXMP_PropValueIsURI = 0x00000002UL, + + // ------------------------------------------------------ + // Options relating to qualifiers attached to a property. + + /// The property has qualifiers, includes \c rdf:type and \c xml:lang. + kXMP_PropHasQualifiers = 0x00000010UL, + + /// This is a qualifier for some other property, includes \c rdf:type and \c xml:lang. + /// Qualifiers can have arbitrary structure, and can themselves have qualifiers. If the + /// qualifier itself has a structured value, this flag is only set for the top node of the + /// qualifier's subtree. + kXMP_PropIsQualifier = 0x00000020UL, + + /// Implies \c #kXMP_PropHasQualifiers, property has \c xml:lang. + kXMP_PropHasLang = 0x00000040UL, + + /// Implies \c #kXMP_PropHasQualifiers, property has \c rdf:type. + kXMP_PropHasType = 0x00000080UL, + + // -------------------------------------------- + // Options relating to the data structure form. + + /// The value is a structure with nested fields. + kXMP_PropValueIsStruct = 0x00000100UL, + + /// The value is an array (RDF alt/bag/seq). The "ArrayIs..." flags identify specific types + /// of array; default is a general unordered array, serialized using an \c rdf:Bag container. + kXMP_PropValueIsArray = 0x00000200UL, + + /// The item order does not matter. + kXMP_PropArrayIsUnordered = kXMP_PropValueIsArray, + + /// Implies \c #kXMP_PropValueIsArray, item order matters. It is serialized using an \c rdf:Seq container. + kXMP_PropArrayIsOrdered = 0x00000400UL, + + /// Implies \c #kXMP_PropArrayIsOrdered, items are alternates. It is serialized using an \c rdf:Alt container. + kXMP_PropArrayIsAlternate = 0x00000800UL, + + // ------------------------------------ + // Additional struct and array options. + + /// Implies \c #kXMP_PropArrayIsAlternate, items are localized text. Each array element is a + /// simple property with an \c xml:lang attribute. + kXMP_PropArrayIsAltText = 0x00001000UL, + + // kXMP_InsertBeforeItem = 0x00004000UL, ! Used by SetXyz functions. + // kXMP_InsertAfterItem = 0x00008000UL, ! Used by SetXyz functions. + + // ---------------------------- + // Other miscellaneous options. + + /// This property is an alias name for another property. This is only returned by + /// \c TXMPMeta::GetProperty() and then only if the property name is simple, not an path expression. + kXMP_PropIsAlias = 0x00010000UL, + + /// This property is the base value (actual) for a set of aliases.This is only returned by + /// \c TXMPMeta::GetProperty() and then only if the property name is simple, not an path expression. + kXMP_PropHasAliases = 0x00020000UL, + + /// The value of this property is "owned" by the application, and should not generally be editable in a UI. + kXMP_PropIsInternal = 0x00040000UL, + + /// The value of this property is not derived from the document content. + kXMP_PropIsStable = 0x00100000UL, + + /// The value of this property is derived from the document content. + kXMP_PropIsDerived = 0x00200000UL, + + // kXMPUtil_AllowCommas = 0x10000000UL, ! Used by TXMPUtils::CatenateArrayItems and ::SeparateArrayItems. + // kXMP_DeleteExisting = 0x20000000UL, ! Used by TXMPMeta::SetXyz functions to delete any pre-existing property. + // kXMP_SchemaNode = 0x80000000UL, ! Returned by iterators - #define to avoid warnings + + // ------------------------------ + // Masks that are multiple flags. + + /// Property type bit-flag mask for all array types + kXMP_PropArrayFormMask = kXMP_PropValueIsArray | kXMP_PropArrayIsOrdered | kXMP_PropArrayIsAlternate | kXMP_PropArrayIsAltText, + + /// Property type bit-flag mask for composite types (array and struct) + kXMP_PropCompositeMask = kXMP_PropValueIsStruct | kXMP_PropArrayFormMask, + + /// Mask for bits that are reserved for transient use by the implementation. + kXMP_ImplReservedMask = 0x70000000L + +}; + +#define kXMP_SchemaNode ((XMP_OptionBits)0x80000000UL) + +/// Option bit flags for the \c TXMPMeta property setting functions. These option bits are shared +/// with the accessor functions: +/// \li \c #kXMP_PropValueIsURI +/// \li \c #kXMP_PropValueIsStruct +/// \li \c #kXMP_PropValueIsArray +/// \li \c #kXMP_PropArrayIsOrdered +/// \li \c #kXMP_PropArrayIsAlternate +/// \li \c #kXMP_PropArrayIsAltText +enum { + + /// Option for array item location: Insert a new item before the given index. + kXMP_InsertBeforeItem = 0x00004000UL, + + /// Option for array item location: Insert a new item after the given index. + kXMP_InsertAfterItem = 0x00008000UL, + + /// Delete any pre-existing property. + kXMP_DeleteExisting = 0x20000000UL, + + /// Bit-flag mask for property-value option bits + kXMP_PropValueOptionsMask = kXMP_PropValueIsURI, + + /// Bit-flag mask for array-item location bits + kXMP_PropArrayLocationMask = kXMP_InsertBeforeItem | kXMP_InsertAfterItem + +}; + +// ------------------------------------------------------------------------------------------------- + +/// Option bit flags for \c TXMPMeta::ParseFromBuffer(). +enum { + + /// Require a surrounding \c x:xmpmeta element. + kXMP_RequireXMPMeta = 0x0001UL, + + /// This is the not last input buffer for this parse stream. + kXMP_ParseMoreBuffers = 0x0002UL, + + /// Do not reconcile alias differences, throw an exception. + kXMP_StrictAliasing = 0x0004UL + +}; + +/// Option bit flags for \c TXMPMeta::SerializeToBuffer(). +enum { + + // *** Option to remove empty struct/array, or leaf with empty value? + + /// Omit the XML packet wrapper. + kXMP_OmitPacketWrapper = 0x0010UL, + + /// Default is a writeable packet. + kXMP_ReadOnlyPacket = 0x0020UL, + + /// Use a compact form of RDF. + kXMP_UseCompactFormat = 0x0040UL, + + /// Use a canonical form of RDF. + kXMP_UseCanonicalFormat = 0x0080UL, + + /// Include a padding allowance for a thumbnail image. + kXMP_IncludeThumbnailPad = 0x0100UL, + + /// The padding parameter is the overall packet length. + kXMP_ExactPacketLength = 0x0200UL, + + /// Omit all formatting whitespace. + kXMP_OmitAllFormatting = 0x0800UL, + + /// Omit the x:xmpmeta element surrounding the rdf:RDF element. + kXMP_OmitXMPMetaElement = 0x1000UL, + + /// Include a rdf Hash and Merged flag in x:xmpmeta element. + kXMP_IncludeRDFHash = 0x2000UL, + + _XMP_LittleEndian_Bit = 0x0001UL, // ! Don't use directly, see the combined values below! + _XMP_UTF16_Bit = 0x0002UL, + _XMP_UTF32_Bit = 0x0004UL, + + /// Bit-flag mask for encoding-type bits + kXMP_EncodingMask = 0x0007UL, + + /// Use UTF8 encoding + kXMP_EncodeUTF8 = 0UL, + + /// Use UTF16 big-endian encoding + kXMP_EncodeUTF16Big = _XMP_UTF16_Bit, + + /// Use UTF16 little-endian encoding + kXMP_EncodeUTF16Little = _XMP_UTF16_Bit | _XMP_LittleEndian_Bit, + + /// Use UTF32 big-endian encoding + kXMP_EncodeUTF32Big = _XMP_UTF32_Bit, + + /// Use UTF13 little-endian encoding + kXMP_EncodeUTF32Little = _XMP_UTF32_Bit | _XMP_LittleEndian_Bit + +}; + +// ------------------------------------------------------------------------------------------------- + +/// Option bit flags for \c TXMPIterator construction. +enum { + + /// The low 8 bits are an enum of what data structure to iterate. + kXMP_IterClassMask = 0x00FFUL, + + /// Iterate the property tree of a TXMPMeta object. + kXMP_IterProperties = 0x0000UL, + + /// Iterate the global alias table. + kXMP_IterAliases = 0x0001UL, + + /// Iterate the global namespace table. + kXMP_IterNamespaces = 0x0002UL, + + /// Just do the immediate children of the root, default is subtree. + kXMP_IterJustChildren = 0x0100UL, + + /// Just do the leaf nodes, default is all nodes in the subtree. + kXMP_IterJustLeafNodes = 0x0200UL, + + /// Return just the leaf part of the path, default is the full path. + kXMP_IterJustLeafName = 0x0400UL, + + /// Omit all qualifiers. + kXMP_IterOmitQualifiers = 0x1000UL + +}; + +/// Option bit flags for \c TXMPIterator::Skip(). +enum { + + /// Skip the subtree below the current node. + kXMP_IterSkipSubtree = 0x0001UL, + + /// Skip the subtree below and remaining siblings of the current node. + kXMP_IterSkipSiblings = 0x0002UL + +}; + +// ------------------------------------------------------------------------------------------------- + +/// Option bit flags for \c TXMPUtils::CatenateArrayItems() and \c TXMPUtils::SeparateArrayItems(). +/// These option bits are shared with the accessor functions: +/// \li \c #kXMP_PropValueIsArray, +/// \li \c #kXMP_PropArrayIsOrdered, +/// \li \c #kXMP_PropArrayIsAlternate, +/// \li \c #kXMP_PropArrayIsAltText +enum { + + /// Allow commas in item values, default is separator. + kXMPUtil_AllowCommas = 0x10000000UL + +}; + +/// Option bit flags for \c TXMPUtils::ApplyTemplate(). +enum { + + /// Do all properties, default is just external properties. + kXMPTemplate_IncludeInternalProperties = 0x0001UL, + + /// Perform a Replace operation, add new properties and modify existing ones. + kXMPTemplate_ReplaceExistingProperties = 0x0002UL, + + /// Similar to Replace, also delete if the template has an empty value. + kXMPTemplate_ReplaceWithDeleteEmpty = 0x0004UL, + + /// Perform an Add operation, add properties if they don't already exist. + kXMPTemplate_AddNewProperties = 0x0008UL, + + /// Perform a Clear operation, keep named properties and delete everything else. + kXMPTemplate_ClearUnnamedProperties = 0x0010UL + +}; + +/// Option bit flags for \c TXMPUtils::RemoveProperties() and \c TXMPUtils::AppendProperties(). +enum { + + /// Do all properties, default is just external properties. + kXMPUtil_DoAllProperties = 0x0001UL, + + /// Replace existing values, default is to leave them. + kXMPUtil_ReplaceOldValues = 0x0002UL, + + /// Delete properties if the new value is empty. + kXMPUtil_DeleteEmptyValues = 0x0004UL, + + /// Include aliases, default is just actual properties. + kXMPUtil_IncludeAliases = 0x0800UL + +}; + +// ================================================================================================= +// Types and Constants for XMPFiles +// ================================ + +/// Seek mode constants for use with XMP_IO and inside XMPFiles library code. +enum SeekMode { kXMP_SeekFromStart, kXMP_SeekFromCurrent, kXMP_SeekFromEnd }; + +/// File format constants for use with XMPFiles. +enum { + + // ! Hex used to avoid gcc warnings. Leave the constants so the text reads big endian. There + // ! seems to be no decent way on UNIX to determine the target endianness at compile time. + // ! Forcing it on the client isn't acceptable. + + // -------------------- + // Public file formats. + + /// Public file format constant: 'PDF ' + kXMP_PDFFile = 0x50444620UL, + /// Public file format constant: 'PS ', general PostScript following DSC conventions + kXMP_PostScriptFile = 0x50532020UL, + /// Public file format constant: 'EPS ', encapsulated PostScript + kXMP_EPSFile = 0x45505320UL, + + /// Public file format constant: 'JPEG' + kXMP_JPEGFile = 0x4A504547UL, + /// Public file format constant: 'JPX ', JPEG 2000, ISO 15444-1 + kXMP_JPEG2KFile = 0x4A505820UL, + /// Public file format constant: 'TIFF' + kXMP_TIFFFile = 0x54494646UL, + /// Public file format constant: 'GIF ' + kXMP_GIFFile = 0x47494620UL, + /// Public file format constant: 'PNG ' + kXMP_PNGFile = 0x504E4720UL, + + /// Public file format constant: 'SWF ' + kXMP_SWFFile = 0x53574620UL, + /// Public file format constant: 'FLA ' + kXMP_FLAFile = 0x464C4120UL, + /// Public file format constant: 'FLV ' + kXMP_FLVFile = 0x464C5620UL, + + /// Public file format constant: 'MOV ', Quicktime + kXMP_MOVFile = 0x4D4F5620UL, + /// Public file format constant: 'AVI ' + kXMP_AVIFile = 0x41564920UL, + /// Public file format constant: 'CIN ', Cineon + kXMP_CINFile = 0x43494E20UL, + /// Public file format constant: 'WAV ' + kXMP_WAVFile = 0x57415620UL, + /// Public file format constant: 'MP3 ' + kXMP_MP3File = 0x4D503320UL, + /// Public file format constant: 'SES ', Audition session + kXMP_SESFile = 0x53455320UL, + /// Public file format constant: 'CEL ', Audition loop + kXMP_CELFile = 0x43454C20UL, + /// Public file format constant: 'MPEG' + kXMP_MPEGFile = 0x4D504547UL, + /// Public file format constant: 'MP2 ' + kXMP_MPEG2File = 0x4D503220UL, + /// Public file format constant: 'MP4 ', ISO 14494-12 and -14 + kXMP_MPEG4File = 0x4D503420UL, + /// Public file format constant: 'MXF ' + kXMP_MXFFile = 0x4D584620UL, + /// Public file format constant: 'WMAV', Windows Media Audio and Video + kXMP_WMAVFile = 0x574D4156UL, + /// Public file format constant: 'AIFF' + kXMP_AIFFFile = 0x41494646UL, + /// Public file format constant: 'RED ', RED file format + kXMP_REDFile = 0x52454420UL, + /// Public file format constant: 'P2 ', a collection not really a single file + kXMP_P2File = 0x50322020UL, + /// Public file format constant: 'XDCF', a collection not really a single file + kXMP_XDCAM_FAMFile = 0x58444346UL, + /// Public file format constant: 'XDCS', a collection not really a single file + kXMP_XDCAM_SAMFile = 0x58444353UL, + /// Public file format constant: 'XDCX', a collection not really a single file + kXMP_XDCAM_EXFile = 0x58444358UL, + /// Public file format constant: 'AVHD', a collection not really a single file + kXMP_AVCHDFile = 0x41564844UL, + /// Public file format constant: 'SHDV', a collection not really a single file + kXMP_SonyHDVFile = 0x53484456UL, + /// Public file format constant: 'CNXF', a collection not really a single file + kXMP_CanonXFFile = 0x434E5846UL, + + /// Public file format constant: 'HTML' + kXMP_HTMLFile = 0x48544D4CUL, + /// Public file format constant: 'XML ' + kXMP_XMLFile = 0x584D4C20UL, + /// Public file format constant: 'text' + kXMP_TextFile = 0x74657874UL, + + // ------------------------------- + // Adobe application file formats. + + /// Adobe application file format constant: 'PSD ' + kXMP_PhotoshopFile = 0x50534420UL, + /// Adobe application file format constant: 'AI ' + kXMP_IllustratorFile = 0x41492020UL, + /// Adobe application file format constant: 'INDD' + kXMP_InDesignFile = 0x494E4444UL, + /// Adobe application file format constant: 'AEP ' + kXMP_AEProjectFile = 0x41455020UL, + /// Adobe application file format constant: 'AET ', After Effects Project Template + kXMP_AEProjTemplateFile = 0x41455420UL, + /// Adobe application file format constant: 'FFX ' + kXMP_AEFilterPresetFile = 0x46465820UL, + /// Adobe application file format constant: 'NCOR' + kXMP_EncoreProjectFile = 0x4E434F52UL, + /// Adobe application file format constant: 'PRPJ' + kXMP_PremiereProjectFile = 0x5052504AUL, + /// Adobe application file format constant: 'PRTL' + kXMP_PremiereTitleFile = 0x5052544CUL, + /// Adobe application file format constant: 'UCF ', Universal Container Format + kXMP_UCFFile = 0x55434620UL, + + // ------- + // Others. + + /// Unknown file format constant: ' ' + kXMP_UnknownFile = 0x20202020UL + +}; + +/// Type for file format identification constants. See \c #kXMP_PDFFile and following. +typedef XMP_Uns32 XMP_FileFormat; + +// ------------------------------------------------------------------------------------------------- + +/// Byte-order masks, do not use directly +enum { + kXMP_CharLittleEndianMask = 1, + kXMP_Char16BitMask = 2, + kXMP_Char32BitMask = 4 +}; + +/// Constants to allow easy testing for 16/32 bit and big/little endian. +enum { + /// 8-bit + kXMP_Char8Bit = 0, + /// 16-bit big-endian + kXMP_Char16BitBig = kXMP_Char16BitMask, + /// 16-bit little-endian + kXMP_Char16BitLittle = kXMP_Char16BitMask | kXMP_CharLittleEndianMask, + /// 32-bit big-endian + kXMP_Char32BitBig = kXMP_Char32BitMask, + /// 32-bit little-endian + kXMP_Char32BitLittle = kXMP_Char32BitMask | kXMP_CharLittleEndianMask, + /// Variable or not-yet-known cases + kXMP_CharUnknown = 1 +}; + +/// \name Macros to test components of the character form mask +/// @{ +/// +/// \def XMP_CharFormIs16Bit +/// \brief Macro reports the encoding of a character. +/// \param f The character to check. +/// +/// \def XMP_CharFormIs32Bit +/// \brief Macro reports the encoding of a character. +/// \param f The character to check. +/// +/// \def XMP_CharFormIsBigEndian +/// \brief Macro reports the byte-order of a character. +/// \param f The character to check. +/// +/// \def XMP_CharFormIsLittleEndian +/// \brief Macro reports the byte-order of a character. +/// \param f The character to check. +/// +/// \def XMP_GetCharSize +/// \brief Macro reports the byte-size of a character. +/// \param f The character to check. +/// +/// \def XMP_CharToSerializeForm +/// \brief Macro converts \c XMP_Uns8 to \c XMP_OptionBits. +/// \param cf The character to convert. +/// +/// \def XMP_CharFromSerializeForm +/// \brief Macro converts \c XMP_OptionBits to \c XMP_Uns8. +/// \param sf The character to convert. +/// +/// @} + +#define XMP_CharFormIs16Bit(f) ( ((int)(f) & kXMP_Char16BitMask) != 0 ) +#define XMP_CharFormIs32Bit(f) ( ((int)(f) & kXMP_Char32BitMask) != 0 ) +#define XMP_CharFormIsBigEndian(f) ( ((int)(f) & kXMP_CharLittleEndianMask) == 0 ) +#define XMP_CharFormIsLittleEndian(f) ( ((int)(f) & kXMP_CharLittleEndianMask) != 0 ) +#define XMP_GetCharSize(f) ( ((int)(f)&6) == 0 ? 1 : (int)(f)&6 ) +#define XMP_CharToSerializeForm(cf) ( (XMP_OptionBits)(cf) ) +#define XMP_CharFromSerializeForm(sf) ( (XMP_Uns8)(sf) ) + +/// \def kXMPFiles_UnknownOffset +/// \brief Constant for an unknown packet offset within a file. +#define kXMPFiles_UnknownOffset ((XMP_Int64)-1) + +/// \def kXMPFiles_UnknownLength +/// \brief Constant for an unknown packet length within a file. +#define kXMPFiles_UnknownLength ((XMP_Int32)-1) + +/// XMP packet description +struct XMP_PacketInfo { + + /// Packet offset in the file in bytes, -1 if unknown. + XMP_Int64 offset; + /// Packet length in the file in bytes, -1 if unknown. + XMP_Int32 length; + /// Packet padding size in bytes, zero if unknown. + XMP_Int32 padSize; // Zero if unknown. + + /// Character format using the values \c kXMP_Char8Bit, \c kXMP_Char16BitBig, etc. + XMP_Uns8 charForm; + /// True if there is a packet wrapper and the trailer says writeable by dumb packet scanners. + XMP_Bool writeable; + /// True if there is a packet wrapper, the "" XML processing instructions. + XMP_Bool hasWrapper; + + /// Padding to make the struct's size be a multiple 4. + XMP_Uns8 pad; + + /// Default constructor. + XMP_PacketInfo() : offset(kXMPFiles_UnknownOffset), length(kXMPFiles_UnknownLength), + padSize(0), charForm(0), writeable(0), hasWrapper(0), pad(0) {}; + +}; + +/// Version of the XMP_PacketInfo type +enum { + /// Version of the XMP_PacketInfo type + kXMP_PacketInfoVersion = 3 +}; + +// ------------------------------------------------------------------------------------------------- + +/// Option bit flags for \c TXMPFiles::Initialize(). +enum { + /// Ignore non-XMP text that uses an undefined "local" encoding. + kXMPFiles_IgnoreLocalText = 0x0002, + /// Combination of flags necessary for server products using XMPFiles. + kXMPFiles_ServerMode = kXMPFiles_IgnoreLocalText +}; + +/// Option bit flags for \c TXMPFiles::GetFormatInfo(). +enum { + + /// Can inject first-time XMP into an existing file. + kXMPFiles_CanInjectXMP = 0x00000001, + + /// Can expand XMP or other metadata in an existing file. + kXMPFiles_CanExpand = 0x00000002, + + /// Can copy one file to another, writing new metadata. + kXMPFiles_CanRewrite = 0x00000004, + + /// Can expand, but prefers in-place update. + kXMPFiles_PrefersInPlace = 0x00000008, + + /// Supports reconciliation between XMP and other forms. + kXMPFiles_CanReconcile = 0x00000010, + + /// Allows access to just the XMP, ignoring other forms. + kXMPFiles_AllowsOnlyXMP = 0x00000020, + + /// File handler returns raw XMP packet information. + kXMPFiles_ReturnsRawPacket = 0x00000040, + + /// The file handler does the file open and close. + kXMPFiles_HandlerOwnsFile = 0x00000100, + + /// The file handler allows crash-safe file updates. + kXMPFiles_AllowsSafeUpdate = 0x00000200, + + /// The file format needs the XMP packet to be read-only. + kXMPFiles_NeedsReadOnlyPacket = 0x00000400, + + /// The file handler uses a "sidecar" file for the XMP. + kXMPFiles_UsesSidecarXMP = 0x00000800, + + /// The format is folder oriented, for example the P2 video format. + kXMPFiles_FolderBasedFormat = 0x00001000, + + /// The file Handler is capable of notifying progress notifications + kXMPFiles_CanNotifyProgress = 0x00002000, + + /// The plugin handler is not capable for delay loading + kXMPFiles_NeedsPreloading = 0x00004000 + +}; + +/// Option bit flags for \c TXMPFiles::OpenFile(). +enum { + + /// Open for read-only access. + kXMPFiles_OpenForRead = 0x00000001, + + /// Open for reading and writing. + kXMPFiles_OpenForUpdate = 0x00000002, + + /// Only the XMP is wanted, allows space/time optimizations. + kXMPFiles_OpenOnlyXMP = 0x00000004, + + /// Force use of the given handler (format), do not even verify the format. + kXMPFiles_ForceGivenHandler = 0x00000008, + + /// Be strict about only attempting to use the designated file handler, no fallback to other handlers. + kXMPFiles_OpenStrictly = 0x00000010, + + /// Require the use of a smart handler. + kXMPFiles_OpenUseSmartHandler = 0x00000020, + + /// Force packet scanning, do not use a smart handler. + kXMPFiles_OpenUsePacketScanning = 0x00000040, + + /// Only packet scan files "known" to need scanning. + kXMPFiles_OpenLimitedScanning = 0x00000080, + + /// Attempt to repair a file opened for update, default is to not open (throw an exception). + kXMPFiles_OpenRepairFile = 0x00000100, + + /// When updating a file, spend the effort necessary to optimize file layout. + kXMPFiles_OptimizeFileLayout = 0x00000200 + +}; + +/// Option bit flags for \c TXMPFiles::CloseFile(). +enum { + /// Write into a temporary file and swap for crash safety. + kXMPFiles_UpdateSafely = 0x0001 +}; + +// ================================================================================================= +// Error notification and Exceptions +// ================================= + +/// \name Error notification and Exceptions +/// @{ +/// +/// From the beginning through version 5.5, XMP Tookit errors result in throwing an \c XMP_Error +/// exception. For the most part exceptions were thrown early and thus API calls aborted as soon as +/// an error was detected. Starting in version 5.5, support has been added for notifications of +/// errors arising in calls to \c TXMPMeta and \c TXMPFiles functions. +/// +/// A client can register an error notification callback function for a \c TXMPMeta or \c TXMPFiles +/// object. This can be done as a global default or individually to each object. The global default +/// applies to all objects created after it is registered. Within the object there is no difference +/// between the global default or explicitly registered callback. The callback function returns a +/// \c bool value indicating if recovery should be attempted (true) or an exception thrown (false). +/// If no callback is registered, a best effort at recovery and continuation will be made with an +/// exception thrown if recovery is not possible. More details can be found in the \c TXMPMeta and +/// \c TXMPFiles documentation. +/// +/// The \c XMP_Error class contains a numeric code and an English explanation. New numeric codes may +/// be added at any time. There are typically many possible explanations for each numeric code. The +/// explanations try to be precise about the specific circumstances causing the error. +/// +/// \note The explanation string is for debugging use only. It must not be shown to users in a +/// final product. It is written for developers not users, and never localized. + +typedef XMP_Uns8 XMP_ErrorSeverity; + +/// Severity codes for error notifications +enum { + /// Partial recovery and continuation is possible. + kXMPErrSev_Recoverable = 0, + /// Recovery is not possible, an exception will be thrown aborting the API call. + kXMPErrSev_OperationFatal = 1, + /// Recovery is not possible, an exception will be thrown, the file is corrupt and possibly unusable. + kXMPErrSev_FileFatal = 2, + /// Recovery is not possible, an exception will be thrown, the entire process should be aborted. + kXMPErrSev_ProcessFatal = 3 +}; + +// ------------------------------------------------------------------------------------------------- +/// The signature of a client-defined callback for TXMPMeta error notifications. +/// +/// @param context A pointer used to carry client-private context. +/// +/// @param severity The severity of the error, see the \c XMP_ErrorSeverity values. +/// +/// @param cause A numeric code for the cause of the error, from the XMP_Error exception codes. +/// Codes used with TXMPMeta error notifications: +/// \li \c kXMPErr_BadXML - An XML syntax error found during parsing. +/// \li \c kXMPErr_BadRDF - A syntax or semantic parsing error in the XMP subset of RDF. +/// \li \c kXMPErr_BadXMP - A semantic XMP data model error. +/// \li \c kXMPErr_BadValue - An XMP value error, wrong type, out of range, etc. +/// \li \c kXMPErr_NoMemory - A heap allocation failure. +/// +/// @param message An explanation of the error, for debugging use only. This should not be displayed +/// to users in a final product. +/// +/// @return True if the operation should continue with a best effort attempt at recovery, false if +/// it should be aborted with an exception thrown from the library back to the original caller. +/// Recovery is possible only if the severity is kXMPErrSev_Recoverable, an exception will be +/// thrown on return from the callback in all other cases. +/// +/// @see \c TXMPMeta::SetDefaultErrorCallback() and \c TXMPMeta::SetErrorCallback() + +typedef bool (* XMPMeta_ErrorCallbackProc) ( void* context, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message ); + +// ------------------------------------------------------------------------------------------------- +/// The signature of a client-defined callback for TXMPFiles error notifications. +/// +/// @param context A pointer used to carry client-private context. +/// +/// @param filePath The path for the file involved in the error. +/// +/// @param severity The severity of the error, see the \c XMP_ErrorSeverity values. +/// +/// @param cause A numeric code for the cause of the error, from the XMP_Error exception codes. +/// Codes used with TXMPFiles error notifications: +/// \li \c kXMPErr_NoFile - A file does not exist +/// \li \c kXMPErr_FilePermission - A file exists but cannot be opened +/// \li \c kXMPErr_FilePathNotAFile - A path exists which is not a file +/// \li \c dXMPErr_RejectedFileExtension - Any Operation called on rejected file extension +/// \li \c KXMPErr_NoFileHandler - No suitable handler is found for the file +/// \li \c kXMPErr_DiskSpace - A file write fails due to lack of disk space +/// \li \c kXMPErr_ReadError - A file read fails +/// \li \c kXMPErr_WriteError - A file write fails for some other reason than space +/// \li \c kXMPErr_BadFileFormat - A file is corrupt or ill-formed +/// \li \c kXMPErr_BadBlockFormat - A portion of a file is corrupt or ill-formed +/// \li \c kXMPErr_BadValue - An XMP or non-XMP metadata item has an invalid value +/// \li \c kXMPErr_NoMemory - A heap allocation failure +/// +/// @param message An explanation of the error, for debugging use only. This should not be displayed +/// to users in a final product. +/// +/// @return True if the operation should continue with a best effort attempt at recovery, false if +/// it should be aborted with an exception thrown from the library back to the original caller. +/// Recovery is possible only if the severity is kXMPErrSev_Recoverable, an exception will be +/// thrown on return from the callback in all other cases. +/// +/// @see \c TXMPFiles::SetDefaultErrorCallback() and \c TXMPFiles::SetErrorCallback() + +typedef bool (* XMPFiles_ErrorCallbackProc) ( void* context, XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message ); + +// ------------------------------------------------------------------------------------------------- +/// Internal: The signatures of client-side wrappers for the error notification callbacks. + +typedef XMP_Bool (* XMPMeta_ErrorCallbackWrapper) ( XMPMeta_ErrorCallbackProc clientProc, void* context, + XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message ); + +typedef XMP_Bool (* XMPFiles_ErrorCallbackWrapper) ( XMPFiles_ErrorCallbackProc clientProc, void* context, + XMP_StringPtr filePath, XMP_ErrorSeverity severity, + XMP_Int32 cause, XMP_StringPtr message ); + +/// XMP Toolkit error, associates an error code with a descriptive error string. +class XMP_Error { +public: + + /// @brief Constructor for an XMP_Error. + /// + /// @param _id The numeric code. + /// + /// @param _errMsg The descriptive string, for debugging use only. It must not be shown to users + /// in a final product. It is written for developers, not users, and never localized. + XMP_Error ( XMP_Int32 _id, XMP_StringPtr _errMsg ) : id(_id), errMsg(_errMsg), notified(false) {}; + + /// Retrieves the numeric code from an XMP_Error. + inline XMP_Int32 GetID() const { return id; }; + + /// Retrieves the descriptive string from an XMP_Error. + inline XMP_StringPtr GetErrMsg() const { return errMsg; }; + + /// Retrieves the information whether particular error is notified or not + inline XMP_Bool IsNotified() const { return notified; } + + /// Sets the notification status for an error + inline void SetNotified() { notified = true; }; + +private: + /// Exception code. See constants \c #kXMPErr_Unknown and following. + XMP_Int32 id; + /// Descriptive string, for debugging use only. It must not be shown to users in a final + /// product. It is written for developers, not users, and never localized. + XMP_StringPtr errMsg; + /// Variable to store whether this particular error is notified to user or not + XMP_Bool notified; +}; + +/// XMP_Error exception code constants +enum { + + // -------------------- + /// Generic error codes. + + /// No error + kXMPErr_NoError = -1, + + /// Generic unknown error + kXMPErr_Unknown = 0, + /// Generic undefined error + kXMPErr_TBD = 1, + /// Generic unavailable error + kXMPErr_Unavailable = 2, + /// Generic bad object error + kXMPErr_BadObject = 3, + /// Generic bad parameter error + kXMPErr_BadParam = 4, + /// Generic bad value error + kXMPErr_BadValue = 5, + /// Generic assertion failure + kXMPErr_AssertFailure = 6, + /// Generic enforcement failure + kXMPErr_EnforceFailure = 7, + /// Generic unimplemented error + kXMPErr_Unimplemented = 8, + /// Generic internal failure + kXMPErr_InternalFailure = 9, + /// Generic deprecated error + kXMPErr_Deprecated = 10, + /// Generic external failure + kXMPErr_ExternalFailure = 11, + /// Generic user abort error + kXMPErr_UserAbort = 12, + /// Generic standard exception + kXMPErr_StdException = 13, + /// Generic unknown exception + kXMPErr_UnknownException = 14, + /// Generic out-of-memory error + kXMPErr_NoMemory = 15, + /// Progress reporting callback requested abort + kXMPErr_ProgressAbort = 16, + + // ------------------------------------ + // More specific parameter error codes. + + /// Bad schema parameter + kXMPErr_BadSchema = 101, + /// Bad XPath parameter + kXMPErr_BadXPath = 102, + /// Bad options parameter + kXMPErr_BadOptions = 103, + /// Bad index parameter + kXMPErr_BadIndex = 104, + /// Bad iteration position + kXMPErr_BadIterPosition = 105, + /// XML parsing error (deprecated) + kXMPErr_BadParse = 106, + /// Serialization error + kXMPErr_BadSerialize = 107, + /// File format error + kXMPErr_BadFileFormat = 108, + /// No file handler found for format + kXMPErr_NoFileHandler = 109, + /// Data too large for JPEG file format + kXMPErr_TooLargeForJPEG = 110, + /// A file does not exist + kXMPErr_NoFile = 111, + /// A file exists but cannot be opened + kXMPErr_FilePermission = 112, + /// A file write failed due to lack of disk space + kXMPErr_DiskSpace = 113, + /// A file read failed + kXMPErr_ReadError = 114, + /// A file write failed for a reason other than lack of disk space + kXMPErr_WriteError = 115, + /// A block of a file is ill-formed, e.g. invalid IPTC-IIM in a photo + kXMPErr_BadBlockFormat = 116, + /// File Path is not a file + kXMPErr_FilePathNotAFile = 117, + /// Rejected File extension + kXMPErr_RejectedFileExtension = 118, + + // ----------------------------------------------- + // File format and internal structure error codes. + + /// XML format error + kXMPErr_BadXML = 201, + /// RDF format error + kXMPErr_BadRDF = 202, + /// XMP format error + kXMPErr_BadXMP = 203, + /// Empty iterator + kXMPErr_EmptyIterator = 204, + /// Unicode error + kXMPErr_BadUnicode = 205, + /// TIFF format error + kXMPErr_BadTIFF = 206, + /// JPEG format error + kXMPErr_BadJPEG = 207, + /// PSD format error + kXMPErr_BadPSD = 208, + /// PSIR format error + kXMPErr_BadPSIR = 209, + /// IPTC format error + kXMPErr_BadIPTC = 210, + /// MPEG format error + kXMPErr_BadMPEG = 211 + +}; + +/// @} + +// ================================================================================================= +// Client callbacks +// ================ + +// ------------------------------------------------------------------------------------------------- +/// \name Special purpose callback functions +/// @{ + +/// A signed 32-bit integer used as a status result for the output callback routine, +/// \c XMP_TextOutputProc. Zero means no error, all other values except -1 are private to the callback. +/// The callback is wrapped to prevent exceptions being thrown across DLL boundaries. Any exceptions +/// thrown out of the callback cause a return status of -1. + +typedef XMP_Int32 XMP_Status; + +// ------------------------------------------------------------------------------------------------- +/// The signature of a client-defined callback for text output from XMP Toolkit debugging +/// operations. The callback is invoked one or more times for each line of output. The end of a line +/// is signaled by a '\\n' character at the end of the buffer. Formatting newlines are never present +/// in the middle of a buffer, but values of properties might contain any UTF-8 characters. +/// +/// @param refCon A pointer to client-defined data passed to the TextOutputProc. +/// +/// @param buffer A string containing one line of output. +/// +/// @param bufferSize The number of characters in the output buffer. +/// +/// @return A success/fail status value. Any failure result aborts the output. +/// +/// @see \c TXMPMeta::DumpObject() + +typedef XMP_Status (* XMP_TextOutputProc) ( void * refCon, + XMP_StringPtr buffer, + XMP_StringLen bufferSize ); + +// ------------------------------------------------------------------------------------------------- +/// The signature of a client-defined callback to check for a user request to abort a time-consuming +/// operation within XMPFiles. +/// +/// @param arg A pointer to caller-defined data passed from the registration call. +/// +/// @return True to abort the current operation, which results in an exception being thrown. +/// +/// @see \c TXMPFiles::SetAbortProc() + +typedef bool (* XMP_AbortProc) ( void * arg ); + +// ------------------------------------------------------------------------------------------------- +/// The signature of a client-defined callback for progress report notifications. +/// +/// @param context A pointer used to carry client-private context. +/// +/// @param elapsedTime The time in seconds since the progress reporting started. +/// +/// @param fractionDone A float value estimating the amount of work already done, in the range of +/// 0.0 to 1.0. A value of 0.0 is given if the amount is not known, this happens if there is no +/// estimate total for the total work. The units of work are not defined, but should usually be +/// related to the number of bytes of I/O. This will go backwards if total work estimate changes. +/// +/// @param secondsToGo A float value estimating the number of seconds left to complete the file +/// operation. A value of 0.0 is given if the amount is not known, this happens if the amount of +/// total work is unknown. This can go backwards according to throughput or if work estimate changes. +/// +/// @return True if the file operation should continue, false if it should be aborted with an +/// exception being thrown from the XMPFiles library back to the original caller. +/// +/// @see \c TXMPFiles::SetDefaultProgressCallback() and \c TXMPFiles::SetProgressCallback() + +typedef bool (* XMP_ProgressReportProc) ( void * context, float elapsedTime, float fractionDone, float secondsToGo ); + +// ------------------------------------------------------------------------------------------------- +/// Internal: The signature of a client-side wrapper for the progress report callback. + +typedef XMP_Bool (* XMP_ProgressReportWrapper) ( XMP_ProgressReportProc proc, void * context, + float elapsedTime, float fractionDone, float secondsToGo ); + +/// @} + +// ================================================================================================= +// Stuff with no better place to be +// ================================ + +/// XMP Toolkit version information +typedef struct XMP_VersionInfo { + /// The primary release number, the "1" in version "1.2.3". + XMP_Uns8 major; + /// The secondary release number, the "2" in version "1.2.3". + XMP_Uns8 minor; + /// The tertiary release number, the "3" in version "1.2.3". + XMP_Uns8 micro; + /// A 0/1 boolean value, true if this is a debug build. + XMP_Bool isDebug; + /// A rolling build number, monotonically increasing in a release. + XMP_Uns32 build; + /// Individual feature implementation flags. + XMP_Uns32 flags; + /// A comprehensive version information string. + XMP_StringPtr message; +} XMP_VersionInfo; + +// ================================================================================================= + +#if __cplusplus +} // extern "C" +#endif + +#include + +#endif // __XMP_Const_h__ diff --git a/source/lib/xmp_core/public/include/XMP_Environment.h b/source/lib/xmp_core/public/include/XMP_Environment.h new file mode 100644 index 0000000..fd459ad --- /dev/null +++ b/source/lib/xmp_core/public/include/XMP_Environment.h @@ -0,0 +1,165 @@ +#ifndef __XMP_Environment_h__ +#define __XMP_Environment_h__ 1 + +// ================================================================================================= +// XMP_Environment.h - Build environment flags for the XMP toolkit. +// ================================================================ +// +// This header is just C preprocessor macro definitions to set up the XMP toolkit build environment. +// It must be the first #include in any chain since it might affect things in other #includes. +// +// ================================================================================================= + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================= +// Determine the Platform +// ================================================================================================= + +#ifdef _WIN32 + + #define XMP_MacBuild 0 + #define XMP_iOSBuild 0 + #define XMP_WinBuild 1 + #define XMP_UNIXBuild 0 + +#elif __APPLE__ + + #include "TargetConditionals.h" + + #if TARGET_OS_IPHONE + + #define XMP_MacBuild 0 + #define XMP_iOSBuild 1 + #define XMP_WinBuild 0 + #define XMP_UNIXBuild 0 + + #else + + #define XMP_MacBuild 1 + #define XMP_iOSBuild 0 + #define XMP_WinBuild 0 + #define XMP_UNIXBuild 0 + + #endif + +#elif __ANDROID__ + +#elif __linux__ || __unix__ + + #define XMP_MacBuild 0 + #define XMP_WinBuild 0 + #define XMP_UNIXBuild 1 + #define XMP_iOSBuild 0 + +#else + #error "XMP environment error - Unknown compiler" +#endif + +// ================================================================================================= +// Common Macros +// ============= + +#if defined ( DEBUG ) + #if defined ( NDEBUG ) + #error "XMP environment error - both DEBUG and NDEBUG are defined" + #endif + #define XMP_DebugBuild 1 +#endif + +#if defined ( NDEBUG ) + #define XMP_DebugBuild 0 +#endif + +#ifndef XMP_DebugBuild + #define XMP_DebugBuild 0 +#endif + +#if XMP_DebugBuild + #include // The assert macro needs printf. +#endif + +#ifndef DISABLE_SERIALIZED_IMPORT_EXPORT + #define DISABLE_SERIALIZED_IMPORT_EXPORT 0 +#endif + +#ifndef XMP_64 + #if _WIN64 || defined(_LP64) + #define XMP_64 1 + #else + #define XMP_64 0 + #endif +#endif + +// ================================================================================================= +// Macintosh Specific Settings +// =========================== +#if (XMP_MacBuild) + #define XMP_HELPER_DLL_IMPORT __attribute__((visibility("default"))) + #define XMP_HELPER_DLL_EXPORT __attribute__((visibility("default"))) + #define XMP_HELPER_DLL_PRIVATE __attribute__((visibility("hidden"))) +#endif + +// ================================================================================================= +// Windows Specific Settings +// ========================= +#if (XMP_WinBuild) + #define XMP_HELPER_DLL_IMPORT + #define XMP_HELPER_DLL_EXPORT + #define XMP_HELPER_DLL_PRIVATE +#endif + +// ================================================================================================= +// UNIX Specific Settings +// ====================== +#if (XMP_UNIXBuild) + #define XMP_HELPER_DLL_IMPORT + #define XMP_HELPER_DLL_EXPORT + #define XMP_HELPER_DLL_PRIVATE +#endif + +// ================================================================================================= +// IOS Specific Settings +// =========================== +#if (XMP_iOSBuild) + #include + #if (TARGET_CPU_ARM) + #define XMP_IOS_ARM 1 + #else + #define XMP_IOS_ARM 0 + #endif + #define XMP_HELPER_DLL_IMPORT __attribute__((visibility("default"))) + #define XMP_HELPER_DLL_EXPORT __attribute__((visibility("default"))) + #define XMP_HELPER_DLL_PRIVATE __attribute__((visibility("hidden"))) +#endif + +// ================================================================================================= +// Banzai Specific Settings +// ====================== +#if (XMP_Banzai) + #define XMP_HELPER_DLL_IMPORT + #define XMP_HELPER_DLL_EXPORT + #define XMP_HELPER_DLL_PRIVATE +#endif + + +// ================================================================================================= + +#if (XMP_DynamicBuild) + #define XMP_PUBLIC XMP_HELPER_DLL_EXPORT + #define XMP_PRIVATE XMP_HELPER_DLL_PRIVATE +#elif (XMP_StaticBuild) + #define XMP_PUBLIC + #define XMP_PRIVATE +#else + #define XMP_PUBLIC XMP_HELPER_DLL_IMPORT + #define XMP_PRIVATE XMP_HELPER_DLL_PRIVATE +#endif + +#endif // __XMP_Environment_h__ diff --git a/source/lib/xmp_core/public/include/XMP_IO.hpp b/source/lib/xmp_core/public/include/XMP_IO.hpp new file mode 100644 index 0000000..d485392 --- /dev/null +++ b/source/lib/xmp_core/public/include/XMP_IO.hpp @@ -0,0 +1,171 @@ +#ifndef __XMP_IO_hpp__ +#define __XMP_IO_hpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2010 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "XMP_Environment.h" // ! XMP_Environment.h must be the first included header. + +#include "XMP_Const.h" + +// ================================================================================================= +/// \class XMP_IO XMP_IO.hpp +/// \brief Abstract base class for client-managed I/O with \c TXMPFiles. +/// +/// \c XMP_IO is an abstract base class for client-managed I/O with \c TXMPFiles. This allows a +/// client to use the embedded metadata processing logic of \c TXMPFiles in cases where a string +/// file path cannot be provided, or where it is impractical to allow \c TXMPFiles to separately +/// open the file and do its own I/O. Although described in terms of files, any form of storage may +/// be used as long as the functions operate as defined. +/// +/// This is not a general purpose I/O class. It contains only the necessary functions needed by the +/// internals of \c TXMPFiles. It is intended to be used as an adaptor for an existing I/O mechanism +/// that the client wants \c TXMPFiles to use. +/// +/// To use \c XMP_IO, a client creates a derived class then uses the form of \c TCMPFiles::OpenFile +/// that takes an \c XMP_IO parameter instead of a string file path. The derived \c XMP_IO object +/// must be ready for use when \c TCMPFiles::OpenFile is called. +/// +/// There are no Open or Close functions in \c XMP_IO, they are specific to each implementation. The +/// derived \c XMP_IO object must be open and ready for use before being passed to \c +/// TXMP_Files::OpenFile, and remain open and ready for use until \c TXMP_Files::CloseFile returns, +/// or some other fatal error occurs. The client has final responsibility for closing and +/// terminating the derived \c XMP_IO object. +// ================================================================================================= + +class XMP_IO { +public: + + // --------------------------------------------------------------------------------------------- + /// @brief Read into a buffer, returning the number of bytes read. + /// + /// Read into a buffer, returning the number of bytes read. Returns the actual number of bytes + /// read. Throws an exception if requireSuccess is true and not enough data is available. + /// Throwing \c XMPError is recommended. The buffer content and I/O position after a throw are + /// undefined. + /// + /// @param buffer A pointer to the buffer. + /// @param count The length of the buffer in bytes. + /// @param readAll True if reading less than the requested amount is a failure. + /// + /// @return Returns the number of bytes read. + + enum { kReadAll = true }; + + virtual XMP_Uns32 Read ( void* buffer, XMP_Uns32 count, bool readAll = false ) = 0; + + inline XMP_Uns32 ReadAll ( void* buffer, XMP_Uns32 bytes ) + { return this->Read ( buffer, bytes, kReadAll ); }; + + // --------------------------------------------------------------------------------------------- + /// @brief Write from a buffer. + /// + /// Write from a buffer, overwriting existing data and extending the file as necesary. All data + /// must be written or an exception thrown. Throwing \c XMPError is recommended. + /// + /// @param buffer A pointer to the buffer. + /// @param count The length of the buffer in bytes. + + virtual void Write ( const void* buffer, XMP_Uns32 count ) = 0; + + // --------------------------------------------------------------------------------------------- + /// @brief Set the I/O position, returning the new absolute offset in bytes. + /// + /// Set the I/O position, returning the new absolute offset in bytes. The offset parameter may + /// be positive or negative. A seek beyond EOF is allowed when writing and extends the file, it + /// is equivalent to seeking to EOF then writing the needed amount of undefined data. A + /// read-only seek beyond EOF throws an exception. Throwing \c XMPError is recommended. + /// + /// @param offset The offset relative to the mode. + /// @param mode The mode, or origin, of the seek. + /// + /// @return The new absolute offset in bytes. + + virtual XMP_Int64 Seek ( XMP_Int64 offset, SeekMode mode ) = 0; + + inline XMP_Int64 Offset() { return this->Seek ( 0, kXMP_SeekFromCurrent ); }; + inline XMP_Int64 Rewind() { return this->Seek ( 0, kXMP_SeekFromStart ); }; // Always returns 0. + inline XMP_Int64 ToEOF() { return this->Seek ( 0, kXMP_SeekFromEnd ); }; + + // --------------------------------------------------------------------------------------------- + /// @brief Return the length of the file in bytes. + /// + /// Return the length of the file in bytes. The I/O position is unchanged. + /// + /// @return The length of the file in bytes. + + virtual XMP_Int64 Length() = 0; + + // --------------------------------------------------------------------------------------------- + /// @brief Truncate the file to the given length. + /// + /// Truncate the file to the given length. The I/O position after truncation is unchanged if + /// still valid, otherwise it is set to the new EOF. Throws an exception if the new length is + /// longer than the file's current length. Throwing \c XMPError is recommended. + /// + /// @param length The new length for the file, must be less than or equal to the current length. + + virtual void Truncate ( XMP_Int64 length ) = 0; + + // --------------------------------------------------------------------------------------------- + /// @brief Create an associated temp file for use in a safe-save style operation. + /// + /// Create an associated temp file, for example in the same directory and with a related name. + /// Returns an already existing temp with no other action. The temp must be opened for + /// read-write access. It will be used in a safe-save style operation, using some of the + /// original file plus new portions to write the temp, then replacing the original from the temp + /// when done. Throws an exception if the owning object is opened for read-only access, or if + /// the temp file cannot be created. Throwing \c XMPError is recommended. + /// + /// The temp file is normally closed and deleted, and the temporary \c XMP_IO object deleted, by + /// a call to \c AbsorbTemp or \c DeleteTemp. It must be closed and deleted by the derived \c + /// XMP_IO object's destructor if necessary. + /// + /// \c DeriveTemp may be called on a temporary \c XMP_IO object. + /// + /// @return A pointer to the associated temporary \c XMP_IO object. + + virtual XMP_IO* DeriveTemp() = 0; + + // --------------------------------------------------------------------------------------------- + /// @brief Replace the owning file's content with that of the temp. + /// + /// Used at the end of a safe-save style operation to replace the original content with that + /// from the associated temp file. The temp file must be closed and deleted after the content + /// swap. The temporary \c XMP_IO object is deleted. Throws an exception if the temp file cannot + /// be absorbed. Throwing \c XMPError is recommended. + + virtual void AbsorbTemp() = 0; + + // --------------------------------------------------------------------------------------------- + /// @brief Delete a temp file, leaving the original alone. + /// + /// Used for a failed safe-save style operation. The temp file is closed and deleted without + /// being absorbed, and the temporary \c XMP_IO object is deleted. Does nothing if no temp + /// exists. + + virtual void DeleteTemp() = 0; + + // --------------------------------------------------------------------------------------------- + + XMP_IO() {}; + virtual ~XMP_IO() {}; + +private: + + // --------------------------------------------------------------------------------------------- + /// Copy construction and assignment are not public. That would require the implementation to + /// share state across multiple XMP_IO objects. + + XMP_IO ( const XMP_IO & original ); + void operator= ( const XMP_IO& in ) { *this = in; /* Avoid Win compile warnings. */ }; + +}; + +#endif // __XMP_IO_hpp__ diff --git a/source/lib/xmp_core/public/include/XMP_Version.h b/source/lib/xmp_core/public/include/XMP_Version.h new file mode 100644 index 0000000..53707b1 --- /dev/null +++ b/source/lib/xmp_core/public/include/XMP_Version.h @@ -0,0 +1,52 @@ +#ifndef __XMP_Version_h__ +#define __XMP_Version_h__ 1 + +/* --------------------------------------------------------------------------------------------- */ +/* ** IMPORTANT ** This file must be usable by strict ANSI C compilers. No "//" comments, etc. */ +/* --------------------------------------------------------------------------------------------- */ + +/* +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= +*/ + +/* ============================================================================================= */ +/** +XMP Toolkit Version Information + +Version information for the XMP toolkit is stored in the executable and available through a runtime +call, SXMPMeta::GetVersionInfo. In addition a static version number is defined in this +header. The information in the executable or returned by SXMPMeta::GetVersionInfo is about +the implementation internals, it is runtime version information. The values defined in this header +describe the version of the API used at client compile time. They do not necessarily relate to the +runtime version. + +Important: Do not display the static values defined here to users as the version of XMP in use. Do +not base runtime decisions on just this static version. It is OK to compare the static and runtime +versions. + +*/ +/* ============================================================================================= */ + +#define XMPCORE_API_VERSION_MAJOR 5 +#define XMPCORE_API_VERSION_MINOR 5 +#define XMPCORE_API_VERSION_MICRO 0 + +#define XMPCORE_API_VERSION 5.5.0 +#define XMPCORE_API_VERSION_STRING "5.5.0" + +#define XMPFILES_API_VERSION_MAJOR 5 +#define XMPFILES_API_VERSION_MINOR 6 +#define XMPFILES_API_VERSION_MICRO 0 + +#define XMPFILES_API_VERSION 5.6.0 +#define XMPFILES_API_VERSION_STRING "5.6.0" + +/* ============================================================================================= */ + +#endif /* __XMP_Version_h__ */ diff --git a/source/lib/xmp_core/public/include/client-glue/TXMPFiles.incl_cpp b/source/lib/xmp_core/public/include/client-glue/TXMPFiles.incl_cpp new file mode 100644 index 0000000..eb19887 --- /dev/null +++ b/source/lib/xmp_core/public/include/client-glue/TXMPFiles.incl_cpp @@ -0,0 +1,484 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================ +/// \file TXMPFiles.incl_cpp +/// \brief The implementation of the TXMPFiles template class. + +#if XMP_WinBuild + #pragma warning ( disable : 4003 ) // not enough actual parameters for macro + #pragma warning ( disable : 4800 ) // forcing value to bool 'true' or 'false' (performance warning) +#endif + +#include "client-glue/WXMP_Common.hpp" + +#include "client-glue/WXMPFiles.hpp" + +// ================================================================================================= +// Implementation Guidelines +// ========================= +// +// The implementations of the template functions are very stylized. The jobs done in this code are: +// +// 1. ... +// +// ================================================================================================= + +#ifndef XMPFiles_TraceCTorDTor + #define XMPFiles_TraceCTorDTor 0 +#endif + +#if XMPFiles_TraceCTorDTor + class XFPeek { // Hack to peek at the client ref count in the internal object. + public: + XFPeek(); + virtual ~XFPeek(); + XMP_Int32 clientRefs; + }; +#endif + +// ================================================================================================= + +XMP_MethodIntro(TXMPFiles,void):: +SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ) +{ + tStringObj * clientStr = (tStringObj*) clientPtr; + clientStr->assign ( valuePtr, valueLen ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +SetClientStringVector ( void * clientPtr, XMP_StringPtr * arrayPtr, XMP_Uns32 stringCount ) +{ + std::vector* clientVec = (std::vector*) clientPtr; + clientVec->clear(); + for ( XMP_Uns32 i = 0; i < stringCount; ++i ) { + tStringObj nextValue ( arrayPtr[i] ); + clientVec->push_back ( nextValue ); + } +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +GetVersionInfo ( XMP_VersionInfo * versionInfo ) +{ + WrapNoCheckVoid ( zXMPFiles_GetVersionInfo_1 ( versionInfo ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +Initialize() +{ + WrapCheckBool ( ok, zXMPFiles_Initialize_1 ( 0 ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +Initialize( const char* pluginFolder, const char* plugins ) +{ + WrapCheckBool ( ok, zXMPFiles_Initialize_2 ( 0, pluginFolder, plugins ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +Initialize ( XMP_OptionBits options ) +{ + WrapCheckBool ( ok, zXMPFiles_Initialize_1 ( options ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +Initialize ( XMP_OptionBits options, const char* pluginFolder, const char* plugins ) +{ + WrapCheckBool ( ok, zXMPFiles_Initialize_2 ( options, pluginFolder, plugins ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +Terminate() +{ + WrapNoCheckVoid ( zXMPFiles_Terminate_1() ); +} + +// ================================================================================================= + +static XMPFilesRef Default_CTor() +{ + WrapCheckXMPFilesRef ( newRef, zXMPFiles_CTor_1() ); + return newRef; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPFiles):: +TXMPFiles() : xmpFilesRef(Default_CTor()) +{ + #if XMPFiles_TraceCTorDTor + XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef; + printf ( "Default construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPFiles):: +TXMPFiles ( const TXMPFiles & original ) : xmpFilesRef(original.xmpFilesRef) +{ + WXMPFiles_IncrementRefCount_1 ( this->xmpFilesRef ); + #if XMPFiles_TraceCTorDTor + XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef; + printf ( "Copy construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +operator= ( const TXMPFiles & rhs ) +{ + #if XMPFiles_TraceCTorDTor + XFPeek* xfLHS = (XFPeek*)this->xmpFilesRef; + XFPeek* xfRHS = (XFPeek*)rhs.xmpFilesRef; + printf ( "Assign TXMPFiles, lhs @ %.8X, rhs @ %.8X\n", this, &rhs ); + printf ( " original lhs ref = %.8X, count = %d\n", xfLHS, xfLHS->clientRefs ); + printf ( " original rhs ref = %.8X, count = %d\n", xfRHS, xfRHS->clientRefs ); + #endif + XMPFilesRef oldRef = this->xmpFilesRef; // ! Decrement last so errors leave client object OK. + this->xmpFilesRef = rhs.xmpFilesRef; + WXMPFiles_IncrementRefCount_1 ( this->xmpFilesRef ); // Increment the count on the new ref. + WXMPFiles_DecrementRefCount_1 ( oldRef ); // Decrement the count on the old ref. + #if XMPFiles_TraceCTorDTor + printf ( " result lhs ref = %.8X, count = %d\n", xfLHS, xfLHS->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPFiles):: +TXMPFiles ( XMPFilesRef _xmpFilesRef ) : xmpFilesRef(_xmpFilesRef) +{ + WXMPFiles_IncrementRefCount_1 ( this->xmpFilesRef ); + #if XMPFiles_TraceCTorDTor + XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef; + printf ( "Ref construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPFiles):: +TXMPFiles ( XMP_StringPtr filePath, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits openFlags /* = 0 */ ) : xmpFilesRef(Default_CTor()) +{ + #if XMPFiles_TraceCTorDTor + XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef; + printf ( "File construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs ); + #endif + bool ok = this->OpenFile ( filePath, format, openFlags ); + if ( ! ok ) throw XMP_Error ( kXMPErr_NoFileHandler, "OpenFile returned false" ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPFiles):: +TXMPFiles ( const tStringObj & filePath, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits openFlags /* = 0 */ ) : xmpFilesRef(Default_CTor()) +{ + #if XMPFiles_TraceCTorDTor + XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef; + printf ( "File construct TXMPFiles @ %.8X, ref = %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs ); + #endif + bool ok = this->OpenFile ( filePath.c_str(), format, openFlags ); + if ( ! ok ) throw XMP_Error ( kXMPErr_NoFileHandler, "OpenFile returned false" ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPFiles):: +~TXMPFiles () throw() +{ + #if XMPFiles_TraceCTorDTor + XFPeek* xfPtr = (XFPeek*)this->xmpFilesRef; + printf ( "Destruct TXMPFiles @ %.8X, ref= %.8X, count = %d\n", this, xfPtr, xfPtr->clientRefs ); + #endif + WXMPFiles_DecrementRefCount_1 ( this->xmpFilesRef ); + this->xmpFilesRef = 0; +} + +// ================================================================================================= + +XMP_MethodIntro(TXMPFiles,bool):: +GetFormatInfo ( XMP_FileFormat format, + XMP_OptionBits * flags ) +{ + WrapCheckBool ( found, zXMPFiles_GetFormatInfo_1 ( format, flags ) ); + return found; +} + +// ================================================================================================= + +XMP_MethodIntro(TXMPFiles,XMPFilesRef):: +GetInternalRef() +{ + return this->xmpFilesRef; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,XMP_FileFormat):: +CheckFileFormat ( XMP_StringPtr filePath ) +{ + WrapCheckFormat ( format, zXMPFiles_CheckFileFormat_1 ( filePath ) ); + return format; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,XMP_FileFormat):: +CheckPackageFormat ( XMP_StringPtr folderPath ) +{ + WrapCheckFormat ( format, zXMPFiles_CheckPackageFormat_1 ( folderPath ) ); + return format; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +GetFileModDate ( XMP_StringPtr filePath, XMP_DateTime * modDate, XMP_FileFormat * format, XMP_OptionBits options ) +{ + WrapCheckBool ( ok, zXMPFiles_GetFileModDate_1 ( filePath, modDate, format, options ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +GetAssociatedResources ( XMP_StringPtr filePath, + std::vector* resourceList, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits options /* = 0 */) +{ + WrapCheckBool ( ok, zXMPFiles_GetAssociatedResources_1 ( filePath, resourceList, format, options, SetClientStringVector ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +IsMetadataWritable ( XMP_StringPtr filePath, + bool * writable, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits options /* = 0 */) +{ + if ( writable) + { + XMP_Bool internalWritable = ConvertBoolToXMP_Bool( *writable ); + WrapCheckBool ( ok, zXMPFiles_IsMetadataWritable_1 ( filePath, &internalWritable, format, options ) ); + *writable = ConvertXMP_BoolToBool( internalWritable ); + return ok; + } + else + { + WrapCheckBool ( ok, zXMPFiles_IsMetadataWritable_1 ( filePath, NULL, format, options ) ); + return ok; + } +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +OpenFile ( XMP_StringPtr filePath, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits openFlags /* = 0 */ ) +{ + WrapCheckBool ( ok, zXMPFiles_OpenFile_1 ( filePath, format, openFlags ) ); + return ok; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +OpenFile ( const tStringObj & filePath, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits openFlags /* = 0 */ ) +{ + return this->OpenFile ( filePath.c_str(), format, openFlags ); +} + +// ------------------------------------------------------------------------------------------------- + +#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds. +XMP_MethodIntro(TXMPFiles,bool):: +OpenFile ( XMP_IO * clientIO, + XMP_FileFormat format /* = kXMP_UnknownFile */, + XMP_OptionBits openFlags /* = 0 */ ) +{ + WrapCheckBool ( ok, zXMPFiles_OpenFile_2 ( clientIO, format, openFlags ) ); + return ok; +} +#endif + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +CloseFile ( XMP_OptionBits closeFlags /* = 0 */ ) +{ + WrapCheckVoid ( zXMPFiles_CloseFile_1 ( closeFlags ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +GetFileInfo ( tStringObj * filePath /* = 0 */, + XMP_OptionBits * openFlags /* = 0 */, + XMP_FileFormat * format /* = 0 */, + XMP_OptionBits * handlerFlags /* = 0 */ ) +{ + WrapCheckBool ( isOpen, zXMPFiles_GetFileInfo_1 ( filePath, openFlags, format, handlerFlags, SetClientString ) ); + return isOpen; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +SetAbortProc ( XMP_AbortProc abortProc, + void * abortArg ) +{ + WrapCheckVoid ( zXMPFiles_SetAbortProc_1 ( abortProc, abortArg ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +GetXMP ( SXMPMeta * xmpObj /* = 0 */, + tStringObj * xmpPacket /* = 0 */, + XMP_PacketInfo * packetInfo /* = 0 */ ) +{ + XMPMetaRef xmpRef = 0; + if ( xmpObj != 0 ) { + SXMPUtils::RemoveProperties ( xmpObj, 0, 0, kXMPUtil_DoAllProperties ); // *** Need an SXMPMeta::Clear method: + xmpRef = xmpObj->GetInternalRef(); + } + + WrapCheckBool ( hasXMP, zXMPFiles_GetXMP_1 ( xmpRef, xmpPacket, packetInfo, SetClientString ) ); + return hasXMP; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +PutXMP ( const SXMPMeta & xmpObj ) +{ + WrapCheckVoid ( zXMPFiles_PutXMP_1 ( xmpObj.GetInternalRef(), 0, 0 ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +PutXMP ( XMP_StringPtr xmpPacket, + XMP_StringLen xmpLength /* = kXMP_UseNullTermination */ ) +{ + WrapCheckVoid ( zXMPFiles_PutXMP_1 ( 0, xmpPacket, xmpLength ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +PutXMP ( const tStringObj & xmpPacket ) +{ + this->PutXMP ( xmpPacket.c_str(), (XMP_StringLen)xmpPacket.size() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +CanPutXMP ( const SXMPMeta & xmpObj ) +{ + WrapCheckBool ( canPut, zXMPFiles_CanPutXMP_1 ( xmpObj.GetInternalRef(), 0, 0 ) ); + return canPut; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +CanPutXMP ( XMP_StringPtr xmpPacket, + XMP_StringLen xmpLength /* = kXMP_UseNullTermination */ ) +{ + WrapCheckBool ( canPut, zXMPFiles_CanPutXMP_1 ( 0, xmpPacket, xmpLength ) ); + return canPut; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,bool):: +CanPutXMP ( const tStringObj & xmpPacket ) +{ + return this->CanPutXMP ( xmpPacket.c_str(), (XMP_StringLen)xmpPacket.size() ); +} + +// ================================================================================================= + +XMP_MethodIntro(TXMPFiles,void):: +SetDefaultProgressCallback ( XMP_ProgressReportProc proc, void * context /* = 0 */, + float interval /* = 1.0 */, bool sendStartStop /* = false */ ) +{ + XMP_Bool internalsendStartStop = ConvertBoolToXMP_Bool( sendStartStop ); + WrapCheckVoid ( zXMPFiles_SetDefaultProgressCallback_1 ( proc, context, interval, internalsendStartStop ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +SetProgressCallback ( XMP_ProgressReportProc proc, void * context /* = 0 */, + float interval /* = 1.0 */, bool sendStartStop /* = false */ ) +{ + XMP_Bool internalsendStartStop = ConvertBoolToXMP_Bool( sendStartStop ); + WrapCheckVoid ( zXMPFiles_SetProgressCallback_1 ( proc, context, interval, internalsendStartStop ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +SetDefaultErrorCallback ( XMPFiles_ErrorCallbackProc proc, + void * context /* = 0 */, XMP_Uns32 limit /*= 1 */ ) +{ + WrapCheckVoid ( zXMPFiles_SetDefaultErrorCallback_1 ( proc, context, limit ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +SetErrorCallback ( XMPFiles_ErrorCallbackProc proc, + void * context /* = 0 */, XMP_Uns32 limit /*= 1 */ ) +{ + WrapCheckVoid ( zXMPFiles_SetErrorCallback_1 ( proc, context, limit ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPFiles,void):: +ResetErrorCallbackLimit ( XMP_Uns32 limit /* = 1 */ ) +{ + WrapCheckVoid ( zXMPFiles_ResetErrorCallbackLimit_1 ( limit ) ); +} + +// ================================================================================================= diff --git a/source/lib/xmp_core/public/include/client-glue/TXMPIterator.incl_cpp b/source/lib/xmp_core/public/include/client-glue/TXMPIterator.incl_cpp new file mode 100644 index 0000000..0b39d01 --- /dev/null +++ b/source/lib/xmp_core/public/include/client-glue/TXMPIterator.incl_cpp @@ -0,0 +1,223 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================ +/// \file TXMPIterator.incl_cpp +/// \brief The implementation of the TXMPIterator template class. + +#include "XMP.hpp" +#include "client-glue/WXMP_Common.hpp" +#include "client-glue/WXMPIterator.hpp" + +// ================================================================================================= +// Implementation Guidelines +// ========================= +// +// The implementations of the template functions are very stylized. The jobs done in this code are: +// +// 1. Set up the xmpIter template data member in the constructors. +// 2. Call through to the appropriate WXMPIterator function. +// 3. Copy returned strings and release the threading lock. +// +// The various kinds of functions follow similar patterns, first assuming no returned string: +// +// Constructors - Use an initializer for the xmpIter data member to call the WXMPIterator constructor. +// Destructor - Let the WXMPIterator destructor be implicitly called for the xmpIter data member. +// Static function - Simply call the corresponding WXMPIterator static function. +// Non-static function - Simply call the corresponding WXMPIterator function using xmpIter. +// +// If a member function has returned strings the code looks roughly like this: +// +// <<>> +// <<>> +// if ( <<>> ) { +// if ( outStr != 0 ) outStr->assign ( outPtr, outLen ); +// <<>> +// } +// return result; +// +// The <<>> is the call to the wrapper, and <<>> is the check and throw +// if the wrapper reports failure. The <<>> check is used to determine if the string +// should actually be assigned. For example, GetProperty can't assign the value if the property +// does not exist. There is no <<>> check if it isn't, well, appropriate. Outputs are +// always passed as explicit pointers, and null can be passed if the string is not wanted. The +// inner implementation holds the threading lock if an output string is returned, regardless of +// whether the client wants it or not (which the implementation does not know). +// +// ================================================================================================= + +#ifndef XMP_TraceCTorDTor + #define XMP_TraceCTorDTor 0 +#endif + +#if XMP_TraceCTorDTor + class XIPeek { // Hack to peek at the client ref count in the internal object. + public: + XIPeek(); + virtual ~XIPeek(); + XMP_Int32 clientRefs; + }; +#endif + +// ------------------------------------------------------------------------------------------------- + +#define PropIterCTor(xmpRef,schemaNS,propName,options) \ + WrapCheckIterRef ( newRef, zXMPIterator_PropCTor_1 ( xmpRef, schemaNS, propName, options ) ); \ + this->iterRef = newRef + +// ------------------------------------------------------------------------------------------------- + +#define TableIterCTor(schemaNS,propName,options) \ + WrapCheckIterRef ( newRef, zXMPIterator_TableCTor_1 ( schemaNS, propName, options ) ); \ + this->iterRef = newRef + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPIterator,void):: +SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ) +{ + tStringObj * clientStr = (tStringObj*) clientPtr; + clientStr->assign ( valuePtr, valueLen ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +TXMPIterator ( const TXMPIterator & original ) : iterRef(original.iterRef) +{ + WXMPIterator_IncrementRefCount_1 ( this->iterRef ); + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Copy construct TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPIterator,void):: +operator= ( const TXMPIterator & rhs ) +{ + #if XMP_TraceCTorDTor + XIPeek* xiLHS = (XIPeek*)this->iterRef; + XIPeek* xiRHS = (XIPeek*)rhs.iterRef; + printf ( "Assign TXMPIterator, lhs @ %.8X, rhs @ %.8X\n", this, &rhs ); + printf ( " original lhs ref = %.8X, count = %d\n", xiLHS, xiLHS->clientRefs ); + printf ( " original rhs ref = %.8X, count = %d\n", xiRHS, xiRHS->clientRefs ); + #endif + XMPIteratorRef oldRef = this->iterRef; // ! Decrement last so errors leave client object OK. + this->iterRef = rhs.iterRef; + WXMPIterator_IncrementRefCount_1 ( this->iterRef ); + WXMPIterator_DecrementRefCount_1 ( oldRef ); + #if XMP_TraceCTorDTor + printf ( " result lhs ref = %.8X, count = %d\n", xiLHS, xiLHS->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +TXMPIterator() : iterRef(0) +{ + throw XMP_Error ( kXMPErr_Unavailable, "No default construction for XMP iterators" ); + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Default construct TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +TXMPIterator ( const TXMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options /* = 0 */ ) : iterRef(0) +{ + PropIterCTor ( xmpObj.GetInternalRef(), schemaNS, propName, options ); + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Construct property TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +TXMPIterator ( const TXMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_OptionBits options /* = 0 */ ) : iterRef(0) +{ + PropIterCTor ( xmpObj.GetInternalRef(), schemaNS, "", options ); + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Construct schema TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +TXMPIterator ( const TXMPMeta & xmpObj, + XMP_OptionBits options /* = 0 */ ) : iterRef(0) +{ + PropIterCTor ( xmpObj.GetInternalRef(), "", "", options ); + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Construct tree TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +TXMPIterator ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options ) : iterRef(0) +{ + TableIterCTor ( schemaNS, propName, options ); + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Construct table TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPIterator):: +~TXMPIterator () throw() +{ + #if XMP_TraceCTorDTor + XIPeek* xiPtr = (XIPeek*)this->iterRef; + printf ( "Destruct TXMPIterator @ %.8X, ref = %.8X, count = %d\n", this, xiPtr, xiPtr->clientRefs ); + #endif + WXMPIterator_DecrementRefCount_1 ( this->iterRef ); + this->iterRef = 0; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPIterator,bool):: +Next ( tStringObj * schemaNS /* = 0 */, + tStringObj * propPath /* = 0 */, + tStringObj * propValue /* = 0 */, + XMP_OptionBits * options /* = 0 */ ) +{ + WrapCheckBool ( found, zXMPIterator_Next_1 ( schemaNS, propPath, propValue, options, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPIterator,void):: +Skip ( XMP_OptionBits options ) +{ + WrapCheckVoid ( zXMPIterator_Skip_1 ( options ) ); +} + +// ================================================================================================= diff --git a/source/lib/xmp_core/public/include/client-glue/TXMPMeta.incl_cpp b/source/lib/xmp_core/public/include/client-glue/TXMPMeta.incl_cpp new file mode 100644 index 0000000..aa5f4b8 --- /dev/null +++ b/source/lib/xmp_core/public/include/client-glue/TXMPMeta.incl_cpp @@ -0,0 +1,914 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================ +/// \file TXMPMeta.incl_cpp +/// \brief The implementation of the TXMPMeta template class. + +#include "XMP.hpp" + +#include "client-glue/WXMP_Common.hpp" + +#include "client-glue/WXMPMeta.hpp" + +#if INCLUDE_XMP_NEW_DOM_MODEL + #include "XMPCore/XMPCore_Defines.h" + + #if ENABLE_NEW_DOM_MODEL + #include "XMPCore/XMPCore_Defines.h" + #include "XMPCore/Interfaces/IXMPDOMFactory.h" + #endif +#endif + +// ================================================================================================= +// Implementation Guidelines +// ========================= +// +// The implementations of the template functions are very stylized. ... +// +// ================================================================================================= + +#ifndef XMP_TraceCTorDTor + #define XMP_TraceCTorDTor 0 +#endif + +#if XMP_TraceCTorDTor + class XMPeek { // Hack to peek at the client ref count in the internal object. + public: + XMPeek(); + virtual ~XMPeek(); + XMP_Int32 clientRefs; + }; +#endif + +// ================================================================================================= +// Local utilities +// =============== + +class TOPW_Info { +public: + XMP_TextOutputProc clientProc; + void * clientRefCon; + TOPW_Info ( XMP_TextOutputProc proc, void * refCon ) : clientProc(proc), clientRefCon(refCon) {}; +private: + TOPW_Info() {}; // ! Hide default constructor. +}; + +static XMP_Status TextOutputProcWrapper ( void * refCon, + XMP_StringPtr buffer, + XMP_StringLen bufferSize ) +{ + try { // Don't let client callback exceptions propagate across DLL boundaries. + TOPW_Info * info = (TOPW_Info*)refCon; + return info->clientProc ( info->clientRefCon, buffer, bufferSize ); + } catch ( ... ) { + return -1; + } +} + +// ================================================================================================= +// Initialization and termination +// ============================== + +XMP_MethodIntro(TXMPMeta,void):: +SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ) +{ + tStringObj * clientStr = (tStringObj*) clientPtr; + clientStr->assign ( valuePtr, valueLen ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +GetVersionInfo ( XMP_VersionInfo * info ) +{ + WrapNoCheckVoid ( zXMPMeta_GetVersionInfo_1 ( info ) ); +} + +// ------------------------------------------------------------------------------------------------- + +#if XMP_TraceClientCalls + FILE * xmpClientLog = stderr; +#endif + +#ifndef XMP_TypeCheck + #if ! XMP_DebugBuild + #define XMP_TypeCheck(e,msg) /* nothing */ + #else + #define XMP_TypeCheck(e,msg) if ( ! (e) ) throw XMP_Error ( kXMPErr_AssertFailure, msg ); + #endif +#endif + +XMP_MethodIntro(TXMPMeta,bool):: +Initialize() +{ + // Verify critical type sizes. + XMP_TypeCheck ( (sizeof(XMP_Int8) == 1), "Size wrong for critical type XMP_Int8" ); + XMP_TypeCheck ( (sizeof(XMP_Int16) == 2), "Size wrong for critical type XMP_Int16" ); + XMP_TypeCheck ( (sizeof(XMP_Int32) == 4), "Size wrong for critical type XMP_Int32" ); + XMP_TypeCheck ( (sizeof(XMP_Int64) == 8), "Size wrong for critical type XMP_Int64" ); + XMP_TypeCheck ( (sizeof(XMP_Uns8) == 1), "Size wrong for critical type XMP_Uns8" ); + XMP_TypeCheck ( (sizeof(XMP_Uns16) == 2), "Size wrong for critical type XMP_Uns16" ); + XMP_TypeCheck ( (sizeof(XMP_Uns32) == 4), "Size wrong for critical type XMP_Uns32" ); + XMP_TypeCheck ( (sizeof(XMP_Uns64) == 8), "Size wrong for critical type XMP_Uns64" ); + XMP_TypeCheck ( (sizeof(XMP_Bool) == 1), "Size wrong for critical type XMP_Bool" ); + + #if XMP_TraceClientCallsToFile + xmpClientLog = fopen ( "XMPClientLog.txt", "w" ); + if ( xmpClientLog == 0 ) xmpClientLog = stderr; + #endif + + WrapCheckBool ( ok, zXMPMeta_Initialize_1() ); + + #if ENABLE_NEW_DOM_MODEL + NS_XMPCOMMON::IXMPDOMFactory_latest::CreateInstance(); + #endif + + return ok; + +} +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +Terminate() +{ + WrapNoCheckVoid ( zXMPMeta_Terminate_1() ); + + #if XMP_TraceClientCallsToFile + if ( xmpClientLog != stderr ) fclose ( xmpClientLog ); + xmpClientLog = stderr; + #endif +} + +// ================================================================================================= +// Constuctors, destructor, operators +// ================================== + +static XMPMetaRef DefaultCTor() +{ + WrapCheckMetaRef ( newRef, zXMPMeta_CTor_1() ); + return newRef; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPMeta):: +TXMPMeta() : xmpRef(DefaultCTor()) +{ + #if XMP_TraceCTorDTor + XMPeek* xmPtr = (XMPeek*)this->xmpRef; + printf ( "Default construct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPMeta):: +TXMPMeta ( const TXMPMeta & original ) : xmpRef(original.xmpRef) +{ + WXMPMeta_IncrementRefCount_1 ( this->xmpRef ); + #if XMP_TraceCTorDTor + XMPeek* xmPtr = (XMPeek*)this->xmpRef; + printf ( "Copy construct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +operator= ( const TXMPMeta & rhs ) +{ + #if XMP_TraceCTorDTor + XMPeek* xmLHS = (XMPeek*)this->xmpRef; + XMPeek* xmRHS = (XMPeek*)rhs.xmpRef; + printf ( "Assign TXMPMeta, lhs @ %.8X, rhs @ %.8X\n", this, &rhs ); + printf ( " original lhs ref = %.8X, count = %d\n", xmLHS, xmLHS->clientRefs ); + printf ( " original rhs ref = %.8X, count = %d\n", xmRHS, xmRHS->clientRefs ); + #endif + XMPMetaRef oldRef = this->xmpRef; // ! Decrement last so errors leave client object OK. + this->xmpRef = rhs.xmpRef; + WXMPMeta_IncrementRefCount_1 ( this->xmpRef ); // Increment the count on the new ref. + WXMPMeta_DecrementRefCount_1 ( oldRef ); // Decrement the count on the old ref. + #if XMP_TraceCTorDTor + printf ( " result lhs ref = %.8X, count = %d\n", xmLHS, xmLHS->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPMeta):: +TXMPMeta ( XMPMetaRef _xmpRef ) : xmpRef(_xmpRef) +{ + WXMPMeta_IncrementRefCount_1 ( this->xmpRef ); + #if XMP_TraceCTorDTor + XMPeek* xmPtr = (XMPeek*)this->xmpRef; + printf ( "Ref construct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPMeta):: +TXMPMeta ( XMP_StringPtr buffer, + XMP_StringLen xmpSize ) : xmpRef(DefaultCTor()) +{ + #if XMP_TraceCTorDTor + XMPeek* xmPtr = (XMPeek*)this->xmpRef; + printf ( "Parse construct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs ); + #endif + try { + this->ParseFromBuffer ( buffer, xmpSize ); + } catch ( ... ) { + WXMPMeta_DecrementRefCount_1 ( this->xmpRef ); + this->xmpRef = 0; + throw; + } +} + +// ------------------------------------------------------------------------------------------------- + +XMP_CTorDTorIntro(TXMPMeta):: +~TXMPMeta() throw() +{ + #if XMP_TraceCTorDTor + XMPeek* xmPtr = (XMPeek*)this->xmpRef; + printf ( "Destruct TXMPMeta @ %.8X, ref = %.8X, count = %d\n", this, xmPtr, xmPtr->clientRefs ); + #endif + WXMPMeta_DecrementRefCount_1 ( this->xmpRef ); + this->xmpRef = 0; + +} // ~TXMPMeta () + +// ================================================================================================= +// Global state functions +// ====================== + +XMP_MethodIntro(TXMPMeta,XMP_OptionBits):: +GetGlobalOptions() +{ + WrapCheckOptions ( options, zXMPMeta_GetGlobalOptions_1() ); + return options; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetGlobalOptions ( XMP_OptionBits options ) +{ + WrapCheckVoid ( zXMPMeta_SetGlobalOptions_1 ( options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,XMP_Status):: +DumpNamespaces ( XMP_TextOutputProc outProc, + void * refCon ) +{ + TOPW_Info info ( outProc, refCon ); + WrapCheckStatus ( status, zXMPMeta_DumpNamespaces_1 ( TextOutputProcWrapper, &info ) ); + return status; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +RegisterNamespace ( XMP_StringPtr namespaceURI, + XMP_StringPtr suggestedPrefix, + tStringObj * registeredPrefix ) +{ + WrapCheckBool ( prefixMatch, zXMPMeta_RegisterNamespace_1 ( namespaceURI, suggestedPrefix, registeredPrefix, SetClientString ) ); + return prefixMatch; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetNamespacePrefix ( XMP_StringPtr namespaceURI, + tStringObj * namespacePrefix ) +{ + WrapCheckBool ( found, zXMPMeta_GetNamespacePrefix_1 ( namespaceURI, namespacePrefix, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetNamespaceURI ( XMP_StringPtr namespacePrefix, + tStringObj * namespaceURI ) +{ + WrapCheckBool ( found, zXMPMeta_GetNamespaceURI_1 ( namespacePrefix, namespaceURI, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +DeleteNamespace ( XMP_StringPtr namespaceURI ) +{ + WrapCheckVoid ( zXMPMeta_DeleteNamespace_1 ( namespaceURI ) ); +} + +// ================================================================================================= +// Basic property manipulation functions +// ===================================== + +XMP_MethodIntro(TXMPMeta,bool):: +GetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + tStringObj * propValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetProperty_1 ( schemaNS, propName, propValue, options, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + tStringObj * itemValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetArrayItem_1 ( schemaNS, arrayName, itemIndex, itemValue, options, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + tStringObj * fieldValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetStructField_1 ( schemaNS, structName, fieldNS, fieldName, fieldValue, options, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + tStringObj * qualValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetQualifier_1 ( schemaNS, propName, qualNS, qualName, qualValue, options, SetClientString ) ); + return found; +} //GetQualifier () + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr propValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetProperty_1 ( schemaNS, propName, propValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const tStringObj & propValue, + XMP_OptionBits options /* = 0 */ ) +{ + this->SetProperty ( schemaNS, propName, propValue.c_str(), options ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_StringPtr itemValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetArrayItem_1 ( schemaNS, arrayName, itemIndex, itemValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + const tStringObj & itemValue, + XMP_OptionBits options /* = 0 */ ) +{ + this->SetArrayItem ( schemaNS, arrayName, itemIndex, itemValue.c_str(), options ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +AppendArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + XMP_StringPtr itemValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_AppendArrayItem_1 ( schemaNS, arrayName, arrayOptions, itemValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +AppendArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + const tStringObj & itemValue, + XMP_OptionBits options /* = 0 */ ) +{ + this->AppendArrayItem ( schemaNS, arrayName, arrayOptions, itemValue.c_str(), options ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetStructField_1 ( schemaNS, structName, fieldNS, fieldName, fieldValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + const tStringObj & fieldValue, + XMP_OptionBits options /* = 0 */ ) +{ + this->SetStructField ( schemaNS, structName, fieldNS, fieldName, fieldValue.c_str(), options ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_StringPtr qualValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetQualifier_1 ( schemaNS, propName, qualNS, qualName, qualValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + const tStringObj & qualValue, + XMP_OptionBits options /* = 0 */ ) +{ + this->SetQualifier ( schemaNS, propName, qualNS, qualName, qualValue.c_str(), options ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +DeleteProperty ( XMP_StringPtr schemaNS, + XMP_StringPtr propName ) +{ + WrapCheckVoid ( zXMPMeta_DeleteProperty_1 ( schemaNS, propName ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +DeleteArrayItem ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex ) +{ + WrapCheckVoid ( zXMPMeta_DeleteArrayItem_1 ( schemaNS, arrayName, itemIndex ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +DeleteStructField ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName ) +{ + WrapCheckVoid ( zXMPMeta_DeleteStructField_1 ( schemaNS, structName, fieldNS, fieldName ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +DeleteQualifier ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName ) +{ + WrapCheckVoid ( zXMPMeta_DeleteQualifier_1 ( schemaNS, propName, qualNS, qualName ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +DoesPropertyExist ( XMP_StringPtr schemaNS, + XMP_StringPtr propName ) const +{ + WrapCheckBool ( exists, zXMPMeta_DoesPropertyExist_1 ( schemaNS, propName ) ); + return exists; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +DoesArrayItemExist ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex ) const +{ + WrapCheckBool ( exists, zXMPMeta_DoesArrayItemExist_1 ( schemaNS, arrayName, itemIndex ) ); + return exists; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +DoesStructFieldExist ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName ) const +{ + WrapCheckBool ( exists, zXMPMeta_DoesStructFieldExist_1 ( schemaNS, structName, fieldNS, fieldName ) ); + return exists; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +DoesQualifierExist ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName ) const +{ + WrapCheckBool ( exists, zXMPMeta_DoesQualifierExist_1 ( schemaNS, propName, qualNS, qualName ) ); + return exists; +} + +// ================================================================================================= +// Specialized Get and Set functions +// ================================= + +XMP_MethodIntro(TXMPMeta,bool):: +GetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + tStringObj * actualLang, + tStringObj * itemValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetLocalizedText_1 ( schemaNS, altTextName, genericLang, specificLang, + actualLang, itemValue, options, SetClientString ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + XMP_StringPtr itemValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetLocalizedText_1 ( schemaNS, altTextName, genericLang, specificLang, itemValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + const tStringObj & itemValue, + XMP_OptionBits options /* = 0 */ ) +{ + this->SetLocalizedText ( schemaNS, altTextName, genericLang, specificLang, itemValue.c_str(), options ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +DeleteLocalizedText ( XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang ) +{ + WrapCheckVoid ( zXMPMeta_DeleteLocalizedText_1 ( schemaNS, altTextName, genericLang, specificLang ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetProperty_Bool ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool * propValue, + XMP_OptionBits * options ) const +{ + XMP_Bool binValue; + WrapCheckBool ( found, zXMPMeta_GetProperty_Bool_1 ( schemaNS, propName, &binValue, options ) ); + if ( found && (propValue != 0) ) *propValue = binValue; + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetProperty_Int ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 * propValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetProperty_Int_1 ( schemaNS, propName, propValue, options ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetProperty_Int64 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 * propValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetProperty_Int64_1 ( schemaNS, propName, propValue, options ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetProperty_Float ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double * propValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetProperty_Float_1 ( schemaNS, propName, propValue, options ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,bool):: +GetProperty_Date ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_DateTime * propValue, + XMP_OptionBits * options ) const +{ + WrapCheckBool ( found, zXMPMeta_GetProperty_Date_1 ( schemaNS, propName, propValue, options ) ); + return found; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty_Bool ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + bool propValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetProperty_Bool_1 ( schemaNS, propName, propValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty_Int ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 propValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetProperty_Int_1 ( schemaNS, propName, propValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty_Int64 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 propValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetProperty_Int64_1 ( schemaNS, propName, propValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty_Float ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double propValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetProperty_Float_1 ( schemaNS, propName, propValue, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetProperty_Date ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const XMP_DateTime & propValue, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetProperty_Date_1 ( schemaNS, propName, propValue, options ) ); +} + +// ================================================================================================= +// Miscellaneous Member Functions +// ============================== + +XMP_MethodIntro(TXMPMeta,XMPMetaRef):: +GetInternalRef() const +{ + return this->xmpRef; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +GetObjectName ( tStringObj * nameStr ) const +{ + WrapCheckVoid ( zXMPMeta_GetObjectName_1 ( nameStr, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetObjectName ( XMP_StringPtr name ) +{ + WrapCheckVoid ( zXMPMeta_SetObjectName_1 ( name ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetObjectName ( tStringObj name ) +{ + this->SetObjectName ( name.c_str() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,XMP_OptionBits):: +GetObjectOptions() const +{ + WrapCheckOptions ( options, zXMPMeta_GetObjectOptions_1() ); + return options; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetObjectOptions ( XMP_OptionBits options ) +{ + WrapCheckVoid ( zXMPMeta_SetObjectOptions_1 ( options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +Sort() +{ + WrapCheckVoid ( zXMPMeta_Sort_1() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +Erase() +{ + WrapCheckVoid ( zXMPMeta_Erase_1() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,TXMPMeta):: +Clone ( XMP_OptionBits options ) const +{ + WrapCheckMetaRef ( cloneRef, zXMPMeta_Clone_1 ( options ) ); + return TXMPMeta ( cloneRef ); // Ref construct will increment the clientRefs. +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,XMP_Index):: +CountArrayItems ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName ) const +{ + WrapCheckIndex ( count, zXMPMeta_CountArrayItems_1 ( schemaNS, arrayName ) ); + return count; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,XMP_Status):: +DumpObject ( XMP_TextOutputProc outProc, + void * refCon ) const +{ + TOPW_Info info ( outProc, refCon ); + WrapCheckStatus ( status, zXMPMeta_DumpObject_1 ( TextOutputProcWrapper, &info ) ); + return status; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +ParseFromBuffer ( XMP_StringPtr buffer, + XMP_StringLen bufferSize, + XMP_OptionBits options /* = 0 */ ) +{ + WrapCheckVoid ( zXMPMeta_ParseFromBuffer_1 ( buffer, bufferSize, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SerializeToBuffer ( tStringObj * pktString, + XMP_OptionBits options, + XMP_StringLen padding, + XMP_StringPtr newline, + XMP_StringPtr indent, + XMP_Index baseIndent /* = 0 */ ) const +{ + WrapCheckVoid ( zXMPMeta_SerializeToBuffer_1 ( pktString, options, padding, newline, indent, baseIndent, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SerializeToBuffer ( tStringObj * pktString, + XMP_OptionBits options /* = 0 */, + XMP_StringLen padding /* = 0 */ ) const +{ + this->SerializeToBuffer ( pktString, options, padding, "", "", 0 ); +} + +// ------------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetDefaultErrorCallback ( XMPMeta_ErrorCallbackProc proc, + void * context /* = 0 */, + XMP_Uns32 limit /* = 1 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetDefaultErrorCallback_1 ( proc, context, limit ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +SetErrorCallback ( XMPMeta_ErrorCallbackProc proc, + void * context /* = 0 */, + XMP_Uns32 limit /* = 1 */ ) +{ + WrapCheckVoid ( zXMPMeta_SetErrorCallback_1 ( proc, context, limit ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPMeta,void):: +ResetErrorCallbackLimit ( XMP_Uns32 limit /* = 1 */ ) +{ + WrapCheckVoid ( zXMPMeta_ResetErrorCallbackLimit_1 ( limit ) ); +} + +// ================================================================================================= diff --git a/source/lib/xmp_core/public/include/client-glue/TXMPUtils.incl_cpp b/source/lib/xmp_core/public/include/client-glue/TXMPUtils.incl_cpp new file mode 100644 index 0000000..2cd5bae --- /dev/null +++ b/source/lib/xmp_core/public/include/client-glue/TXMPUtils.incl_cpp @@ -0,0 +1,445 @@ +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +// ================================================================================================ +/// \file TXMPUtils.incl_cpp +/// \brief The implementation of the TXMPUtils template class. + +#include "XMP.hpp" +#include "client-glue/WXMP_Common.hpp" +#include "client-glue/WXMPUtils.hpp" + +// ================================================================================================= +// Implementation Guidelines +// ========================= +// +// The implementations of the template functions are very stylized. ... +// +// ================================================================================================= + +XMP_MethodIntro(TXMPUtils,void):: +SetClientString ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ) +{ + tStringObj * clientStr = (tStringObj*) clientPtr; + clientStr->assign ( valuePtr, valueLen ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeArrayItemPath ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + tStringObj * fullPath ) +{ + WrapCheckVoid ( zXMPUtils_ComposeArrayItemPath_1 ( schemaNS, arrayName, itemIndex, fullPath, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeStructFieldPath ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + tStringObj * fullPath ) +{ + WrapCheckVoid ( zXMPUtils_ComposeStructFieldPath_1 ( schemaNS, structName, fieldNS, fieldName, fullPath, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeQualifierPath ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + tStringObj * fullPath ) +{ + WrapCheckVoid ( zXMPUtils_ComposeQualifierPath_1 ( schemaNS, propName, qualNS, qualName, fullPath, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeLangSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr langName, + tStringObj * fullPath ) +{ + WrapCheckVoid ( zXMPUtils_ComposeLangSelector_1 ( schemaNS, arrayName, langName, fullPath, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeLangSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + const tStringObj & langName, + tStringObj * fullPath ) +{ + TXMPUtils::ComposeLangSelector ( schemaNS, arrayName, langName.c_str(), fullPath ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeFieldSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + tStringObj * fullPath ) +{ + WrapCheckVoid ( zXMPUtils_ComposeFieldSelector_1 ( schemaNS, arrayName, fieldNS, fieldName, fieldValue, fullPath, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ComposeFieldSelector ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + const tStringObj & fieldValue, + tStringObj * fullPath ) +{ + TXMPUtils::ComposeFieldSelector ( schemaNS, arrayName, fieldNS, fieldName, fieldValue.c_str(), fullPath ); +} + +// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertFromBool ( bool binValue, + tStringObj * strValue ) +{ + WrapCheckVoid ( zXMPUtils_ConvertFromBool_1 ( binValue, strValue, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertFromInt ( long binValue, + XMP_StringPtr format, + tStringObj * strValue ) +{ + #if XMP_MacBuild & XMP_64 // This is checked because on Mac 64 bit environment, long is of 64 bit and hence gives a warning during implicit + // typecasting to XMP_Int32. Now doing it explicitly in that case. + WrapCheckVoid ( zXMPUtils_ConvertFromInt_1 ( (XMP_Int32)binValue, format, strValue, SetClientString ) ); + #else + WrapCheckVoid ( zXMPUtils_ConvertFromInt_1 ( binValue, format, strValue, SetClientString ) ); + #endif +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertFromInt64 ( long long binValue, + XMP_StringPtr format, + tStringObj * strValue ) +{ + WrapCheckVoid ( zXMPUtils_ConvertFromInt64_1 ( binValue, format, strValue, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertFromFloat ( double binValue, + XMP_StringPtr format, + tStringObj * strValue ) +{ + WrapCheckVoid ( zXMPUtils_ConvertFromFloat_1 ( binValue, format, strValue, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertFromDate ( const XMP_DateTime & binValue, + tStringObj * strValue ) +{ + WrapCheckVoid ( zXMPUtils_ConvertFromDate_1 ( binValue, strValue, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,bool):: +ConvertToBool ( XMP_StringPtr strValue ) +{ + WrapCheckBool ( value, zXMPUtils_ConvertToBool_1 ( strValue ) ); + return value; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,bool):: +ConvertToBool ( const tStringObj & strValue ) +{ + return TXMPUtils::ConvertToBool ( strValue.c_str() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,long):: +ConvertToInt ( XMP_StringPtr strValue ) +{ + WrapCheckInt32 ( value, zXMPUtils_ConvertToInt_1 ( strValue ) ); + return value; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,long):: +ConvertToInt ( const tStringObj & strValue ) +{ + return TXMPUtils::ConvertToInt ( strValue.c_str() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,long long):: +ConvertToInt64 ( XMP_StringPtr strValue ) +{ + WrapCheckInt64 ( value, zXMPUtils_ConvertToInt64_1 ( strValue ) ); + return value; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,long long):: +ConvertToInt64 ( const tStringObj & strValue ) +{ + return TXMPUtils::ConvertToInt64 ( strValue.c_str() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,double):: +ConvertToFloat ( XMP_StringPtr strValue ) +{ + WrapCheckFloat ( value, zXMPUtils_ConvertToFloat_1 ( strValue ) ); + return value; +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,double):: +ConvertToFloat ( const tStringObj & strValue ) +{ + return TXMPUtils::ConvertToFloat ( strValue.c_str() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertToDate ( XMP_StringPtr strValue, + XMP_DateTime * binValue ) +{ + WrapCheckVoid ( zXMPUtils_ConvertToDate_1 ( strValue, binValue ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertToDate ( const tStringObj & strValue, + XMP_DateTime * binValue ) +{ + TXMPUtils::ConvertToDate ( strValue.c_str(), binValue ); +} + +// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +CurrentDateTime ( XMP_DateTime * time ) +{ + WrapCheckVoid ( zXMPUtils_CurrentDateTime_1 ( time ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +SetTimeZone ( XMP_DateTime * time ) +{ + WrapCheckVoid ( zXMPUtils_SetTimeZone_1 ( time ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertToUTCTime ( XMP_DateTime * time ) +{ + WrapCheckVoid ( zXMPUtils_ConvertToUTCTime_1 ( time ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ConvertToLocalTime ( XMP_DateTime * time ) +{ + WrapCheckVoid ( zXMPUtils_ConvertToLocalTime_1 ( time ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,int):: +CompareDateTime ( const XMP_DateTime & left, + const XMP_DateTime & right ) +{ + WrapCheckInt32 ( result, zXMPUtils_CompareDateTime_1 ( left, right ) ); + return result; +} + +// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +EncodeToBase64 ( XMP_StringPtr rawStr, + XMP_StringLen rawLen, + tStringObj * encodedStr ) +{ + WrapCheckVoid ( zXMPUtils_EncodeToBase64_1 ( rawStr, rawLen, encodedStr, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +EncodeToBase64 ( const tStringObj & rawStr, + tStringObj * encodedStr ) +{ + TXMPUtils::EncodeToBase64 ( rawStr.c_str(), (XMP_StringLen)rawStr.size(), encodedStr ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +DecodeFromBase64 ( XMP_StringPtr encodedStr, + XMP_StringLen encodedLen, + tStringObj * rawStr ) +{ + WrapCheckVoid ( zXMPUtils_DecodeFromBase64_1 ( encodedStr, encodedLen, rawStr, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +DecodeFromBase64 ( const tStringObj & encodedStr, + tStringObj * rawStr ) +{ + TXMPUtils::DecodeFromBase64 ( encodedStr.c_str(), (XMP_StringLen)encodedStr.size(), rawStr ); +} + +// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +PackageForJPEG ( const TXMPMeta & xmpObj, + tStringObj * standardXMP, + tStringObj * extendedXMP, + tStringObj * extendedDigest ) +{ + WrapCheckVoid ( zXMPUtils_PackageForJPEG_1 ( xmpObj.GetInternalRef(), standardXMP, extendedXMP, extendedDigest, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +MergeFromJPEG ( TXMPMeta * fullXMP, + const TXMPMeta & extendedXMP ) +{ + WrapCheckVoid ( zXMPUtils_MergeFromJPEG_1 ( fullXMP->GetInternalRef(), extendedXMP.GetInternalRef() ) ); +} + +// ------------------------------------------------------------------------------------------------- +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +CatenateArrayItems ( const TXMPMeta & xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr separator, + XMP_StringPtr quotes, + XMP_OptionBits options, + tStringObj * catedStr ) +{ + WrapCheckVoid ( zXMPUtils_CatenateArrayItems_1 ( xmpObj.GetInternalRef(), schemaNS, arrayName, + separator, quotes, options, catedStr, SetClientString ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +SeparateArrayItems ( TXMPMeta * xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + XMP_StringPtr catedStr ) +{ + if ( xmpObj == 0 ) throw XMP_Error ( kXMPErr_BadParam, "Null output SXMPMeta pointer" ); + WrapCheckVoid ( zXMPUtils_SeparateArrayItems_1 ( xmpObj->GetInternalRef(), schemaNS, arrayName, options, catedStr ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +SeparateArrayItems ( TXMPMeta * xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + const tStringObj & catedStr ) +{ + TXMPUtils::SeparateArrayItems ( xmpObj, schemaNS, arrayName, options, catedStr.c_str() ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +ApplyTemplate ( TXMPMeta * workingXMP, + const TXMPMeta & templateXMP, + XMP_OptionBits actions ) +{ + if ( workingXMP == 0 ) throw XMP_Error ( kXMPErr_BadParam, "Null working SXMPMeta pointer" ); + WrapCheckVoid ( zXMPUtils_ApplyTemplate_1 ( workingXMP->GetInternalRef(), templateXMP.GetInternalRef(), actions ) ); +} + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +RemoveProperties ( TXMPMeta * xmpObj, + XMP_StringPtr schemaNS /* = 0 */, + XMP_StringPtr propName /* = 0 */, + XMP_OptionBits options /* = 0 */ ) +{ + if ( xmpObj == 0 ) throw XMP_Error ( kXMPErr_BadParam, "Null output SXMPMeta pointer" ); + WrapCheckVoid ( zXMPUtils_RemoveProperties_1 ( xmpObj->GetInternalRef(), schemaNS, propName, options ) ); +} + +// ------------------------------------------------------------------------------------------------- + +// ------------------------------------------------------------------------------------------------- + +XMP_MethodIntro(TXMPUtils,void):: +DuplicateSubtree ( const TXMPMeta & source, + TXMPMeta * dest, + XMP_StringPtr sourceNS, + XMP_StringPtr sourceRoot, + XMP_StringPtr destNS /*= 0 */, + XMP_StringPtr destRoot /* = 0 */, + XMP_OptionBits options /* = 0 */ ) +{ + if ( dest == 0 ) throw XMP_Error ( kXMPErr_BadParam, "Null output SXMPMeta pointer" ); + WrapCheckVoid ( zXMPUtils_DuplicateSubtree_1 ( source.GetInternalRef(), dest->GetInternalRef(), + sourceNS, sourceRoot, destNS, destRoot, options ) ); +} + +// ================================================================================================= + +// ================================================================================================= diff --git a/source/lib/xmp_core/public/include/client-glue/WXMPFiles.hpp b/source/lib/xmp_core/public/include/client-glue/WXMPFiles.hpp new file mode 100644 index 0000000..648a842 --- /dev/null +++ b/source/lib/xmp_core/public/include/client-glue/WXMPFiles.hpp @@ -0,0 +1,281 @@ +#ifndef __WXMPFiles_hpp__ +#define __WXMPFiles_hpp__ 1 + +// ================================================================================================= +// ADOBE SYSTEMS INCORPORATED +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "client-glue/WXMP_Common.hpp" + +#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds. + #include "XMP_IO.hpp" +#endif + +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= +/// \file WXMPFiles.h +/// \brief High level support to access metadata in files of interest to Adobe applications. +/// +/// This header ... +/// +// ================================================================================================= + +// ================================================================================================= + +#define WrapCheckXMPFilesRef(result,WCallProto) \ + WXMP_Result wResult; \ + WCallProto; \ + PropagateException ( wResult ); \ + XMPFilesRef result = XMPFilesRef(wResult.ptrResult) + +static XMP_Bool WrapProgressReport ( XMP_ProgressReportProc proc, void * context, + float elapsedTime, float fractionDone, float secondsToGo ) +{ + bool ok; + try { + ok = (*proc) ( context, elapsedTime, fractionDone, secondsToGo ); + } catch ( ... ) { + ok = false; + } + return ConvertBoolToXMP_Bool( ok ); +} + +// ================================================================================================= + +static XMP_Bool WrapFilesErrorNotify ( XMPFiles_ErrorCallbackProc proc, void * context, + XMP_StringPtr filePath, XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message ) +{ + bool ok; + try { + ok = (*proc) ( context, filePath, severity, cause, message ); + } catch ( ... ) { + ok = false; + } + return ConvertBoolToXMP_Bool( ok ); +} + +// ================================================================================================= + +#define zXMPFiles_GetVersionInfo_1(versionInfo) \ + WXMPFiles_GetVersionInfo_1 ( versionInfo /* no wResult */ ) + +#define zXMPFiles_Initialize_1(options) \ + WXMPFiles_Initialize_1 ( options, &wResult ) + +#define zXMPFiles_Initialize_2(options,pluginFolder,plugins) \ + WXMPFiles_Initialize_2 ( options, pluginFolder, plugins, &wResult ) + +#define zXMPFiles_Terminate_1() \ + WXMPFiles_Terminate_1 ( /* no wResult */ ) + +#define zXMPFiles_CTor_1() \ + WXMPFiles_CTor_1 ( &wResult ) + +#define zXMPFiles_GetFormatInfo_1(format,flags) \ + WXMPFiles_GetFormatInfo_1 ( format, flags, &wResult ) + +#define zXMPFiles_CheckFileFormat_1(filePath) \ + WXMPFiles_CheckFileFormat_1 ( filePath, &wResult ) + +#define zXMPFiles_CheckPackageFormat_1(folderPath) \ + WXMPFiles_CheckPackageFormat_1 ( folderPath, &wResult ) + +#define zXMPFiles_GetFileModDate_1(filePath,modDate,format,options) \ + WXMPFiles_GetFileModDate_1 ( filePath, modDate, format, options, &wResult ) + +#define zXMPFiles_GetAssociatedResources_1( filePath, resourceList, format, options, SetClientStringVector ) \ + WXMPFiles_GetAssociatedResources_1 ( filePath, resourceList, format, options, SetClientStringVector, &wResult ) + +#define zXMPFiles_IsMetadataWritable_1( filePath, writable, format, options ) \ + WXMPFiles_IsMetadataWritable_1 ( filePath, writable, format, options, &wResult ) + +#define zXMPFiles_OpenFile_1(filePath,format,openFlags) \ + WXMPFiles_OpenFile_1 ( this->xmpFilesRef, filePath, format, openFlags, &wResult ) + +#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds. +#define zXMPFiles_OpenFile_2(clientIO,format,openFlags) \ + WXMPFiles_OpenFile_2 ( this->xmpFilesRef, clientIO, format, openFlags, &wResult ) +#endif + +#define zXMPFiles_CloseFile_1(closeFlags) \ + WXMPFiles_CloseFile_1 ( this->xmpFilesRef, closeFlags, &wResult ) + +#define zXMPFiles_GetFileInfo_1(clientPath,openFlags,format,handlerFlags,SetClientString) \ + WXMPFiles_GetFileInfo_1 ( this->xmpFilesRef, clientPath, openFlags, format, handlerFlags, SetClientString, &wResult ) + +#define zXMPFiles_SetAbortProc_1(abortProc,abortArg) \ + WXMPFiles_SetAbortProc_1 ( this->xmpFilesRef, abortProc, abortArg, &wResult ) + +#define zXMPFiles_GetXMP_1(xmpRef,clientPacket,packetInfo,SetClientString) \ + WXMPFiles_GetXMP_1 ( this->xmpFilesRef, xmpRef, clientPacket, packetInfo, SetClientString, &wResult ) + +#define zXMPFiles_PutXMP_1(xmpRef,xmpPacket,xmpPacketLen) \ + WXMPFiles_PutXMP_1 ( this->xmpFilesRef, xmpRef, xmpPacket, xmpPacketLen, &wResult ) + +#define zXMPFiles_CanPutXMP_1(xmpRef,xmpPacket,xmpPacketLen) \ + WXMPFiles_CanPutXMP_1 ( this->xmpFilesRef, xmpRef, xmpPacket, xmpPacketLen, &wResult ) + +#define zXMPFiles_SetDefaultProgressCallback_1(proc,context,interval,sendStartStop) \ + WXMPFiles_SetDefaultProgressCallback_1 ( WrapProgressReport, proc, context, interval, sendStartStop, &wResult ) + +#define zXMPFiles_SetProgressCallback_1(proc,context,interval,sendStartStop) \ + WXMPFiles_SetProgressCallback_1 ( this->xmpFilesRef, WrapProgressReport, proc, context, interval, sendStartStop, &wResult ) + +#define zXMPFiles_SetDefaultErrorCallback_1(proc,context,limit) \ + WXMPFiles_SetDefaultErrorCallback_1 ( WrapFilesErrorNotify, proc, context, limit, &wResult ) + +#define zXMPFiles_SetErrorCallback_1(proc,context,limit) \ + WXMPFiles_SetErrorCallback_1 ( this->xmpFilesRef, WrapFilesErrorNotify, proc, context, limit, &wResult ) + +#define zXMPFiles_ResetErrorCallbackLimit_1(limit) \ + WXMPFiles_ResetErrorCallbackLimit_1 ( this->xmpFilesRef, limit, &wResult ) + +// ================================================================================================= + +extern void WXMPFiles_GetVersionInfo_1 ( XMP_VersionInfo * versionInfo ); + +extern void WXMPFiles_Initialize_1 ( XMP_OptionBits options, + WXMP_Result * result ); + +extern void WXMPFiles_Initialize_2 ( XMP_OptionBits options, + const char* pluginFolder, + const char* plugins, + WXMP_Result * result ); + +extern void WXMPFiles_Terminate_1(); + +extern void WXMPFiles_CTor_1 ( WXMP_Result * result ); + +extern void WXMPFiles_IncrementRefCount_1 ( XMPFilesRef xmpFilesRef ); + +extern void WXMPFiles_DecrementRefCount_1 ( XMPFilesRef xmpFilesRef ); + +extern void WXMPFiles_GetFormatInfo_1 ( XMP_FileFormat format, + XMP_OptionBits * flags, // ! Can be null. + WXMP_Result * result ); + +extern void WXMPFiles_CheckFileFormat_1 ( XMP_StringPtr filePath, + WXMP_Result * result ); + +extern void WXMPFiles_CheckPackageFormat_1 ( XMP_StringPtr folderPath, + WXMP_Result * result ); + +extern void WXMPFiles_GetFileModDate_1 ( XMP_StringPtr filePath, + XMP_DateTime * modDate, + XMP_FileFormat * format, // ! Can be null. + XMP_OptionBits options, + WXMP_Result * result ); + + +extern void WXMPFiles_GetAssociatedResources_1 ( XMP_StringPtr filePath, + void * resourceList, + XMP_FileFormat format, + XMP_OptionBits options, + SetClientStringVectorProc SetClientStringVector, + WXMP_Result * result ); + +extern void WXMPFiles_IsMetadataWritable_1 ( XMP_StringPtr filePath, + XMP_Bool * writable, + XMP_FileFormat format, + XMP_OptionBits options, + WXMP_Result * result ); + +extern void WXMPFiles_OpenFile_1 ( XMPFilesRef xmpFilesRef, + XMP_StringPtr filePath, + XMP_FileFormat format, + XMP_OptionBits openFlags, + WXMP_Result * result ); + +#if XMP_StaticBuild // ! Client XMP_IO objects can only be used in static builds. +extern void WXMPFiles_OpenFile_2 ( XMPFilesRef xmpFilesRef, + XMP_IO * clientIO, + XMP_FileFormat format, + XMP_OptionBits openFlags, + WXMP_Result * result ); +#endif + +extern void WXMPFiles_CloseFile_1 ( XMPFilesRef xmpFilesRef, + XMP_OptionBits closeFlags, + WXMP_Result * result ); + +extern void WXMPFiles_GetFileInfo_1 ( XMPFilesRef xmpFilesRef, + void * clientPath, + XMP_OptionBits * openFlags, // ! Can be null. + XMP_FileFormat * format, // ! Can be null. + XMP_OptionBits * handlerFlags, // ! Can be null. + SetClientStringProc SetClientString, + WXMP_Result * result ); + +extern void WXMPFiles_SetAbortProc_1 ( XMPFilesRef xmpFilesRef, + XMP_AbortProc abortProc, + void * abortArg, + WXMP_Result * result ); + +extern void WXMPFiles_GetXMP_1 ( XMPFilesRef xmpFilesRef, + XMPMetaRef xmpRef, // ! Can be null. + void * clientPacket, + XMP_PacketInfo * packetInfo, // ! Can be null. + SetClientStringProc SetClientString, + WXMP_Result * result ); + +extern void WXMPFiles_PutXMP_1 ( XMPFilesRef xmpFilesRef, + XMPMetaRef xmpRef, // ! Only one of the XMP object or packet are passed. + XMP_StringPtr xmpPacket, + XMP_StringLen xmpPacketLen, + WXMP_Result * result ); + +extern void WXMPFiles_CanPutXMP_1 ( XMPFilesRef xmpFilesRef, + XMPMetaRef xmpRef, // ! Only one of the XMP object or packet are passed. + XMP_StringPtr xmpPacket, + XMP_StringLen xmpPacketLen, + WXMP_Result * result ); + +extern void WXMPFiles_SetDefaultProgressCallback_1 ( XMP_ProgressReportWrapper wrapperproc, + XMP_ProgressReportProc clientProc, + void * context, + float interval, + XMP_Bool sendStartStop, + WXMP_Result * result ); + +extern void WXMPFiles_SetProgressCallback_1 ( XMPFilesRef xmpFilesRef, + XMP_ProgressReportWrapper wrapperproc, + XMP_ProgressReportProc clientProc, + void * context, + float interval, + XMP_Bool sendStartStop, + WXMP_Result * result ); + +// ------------------------------------------------------------------------------------------------- + +extern void WXMPFiles_SetDefaultErrorCallback_1 ( XMPFiles_ErrorCallbackWrapper wrapperProc, + XMPFiles_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit, + WXMP_Result * wResult ); + +extern void WXMPFiles_SetErrorCallback_1 ( XMPFilesRef xmpRef, + XMPFiles_ErrorCallbackWrapper wrapperProc, + XMPFiles_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit, + WXMP_Result * wResult ); + +extern void WXMPFiles_ResetErrorCallbackLimit_1 ( XMPFilesRef xmpRef, + XMP_Uns32 limit, + WXMP_Result * wResult ); + +// ================================================================================================= + +#if __cplusplus +} +#endif + +#endif // __WXMPFiles_hpp__ diff --git a/source/lib/xmp_core/public/include/client-glue/WXMPIterator.hpp b/source/lib/xmp_core/public/include/client-glue/WXMPIterator.hpp new file mode 100644 index 0000000..e40a1d4 --- /dev/null +++ b/source/lib/xmp_core/public/include/client-glue/WXMPIterator.hpp @@ -0,0 +1,74 @@ +#if ! __WXMPIterator_hpp__ +#define __WXMPIterator_hpp__ 1 + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "client-glue/WXMP_Common.hpp" + +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= + +#define zXMPIterator_PropCTor_1(xmpRef,schemaNS,propName,options) \ + WXMPIterator_PropCTor_1 ( xmpRef, schemaNS, propName, options, &wResult ); + +#define zXMPIterator_TableCTor_1(schemaNS,propName,options) \ + WXMPIterator_TableCTor_1 ( schemaNS, propName, options, &wResult ); + + +#define zXMPIterator_Next_1(schemaNS,propPath,propValue,options,SetClientString) \ + WXMPIterator_Next_1 ( this->iterRef, schemaNS, propPath, propValue, options, SetClientString, &wResult ); + +#define zXMPIterator_Skip_1(options) \ + WXMPIterator_Skip_1 ( this->iterRef, options, &wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPIterator_PropCTor_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPIterator_TableCTor_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPIterator_IncrementRefCount_1 ( XMPIteratorRef iterRef ); + +extern void +XMP_PUBLIC WXMPIterator_DecrementRefCount_1 ( XMPIteratorRef iterRef ); + +extern void +XMP_PUBLIC WXMPIterator_Next_1 ( XMPIteratorRef iterRef, + void * schemaNS, + void * propPath, + void * propValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPIterator_Skip_1 ( XMPIteratorRef iterRef, + XMP_OptionBits options, + WXMP_Result * wResult ); + +// ================================================================================================= + +#if __cplusplus +} /* extern "C" */ +#endif + +#endif // __WXMPIterator_hpp__ diff --git a/source/lib/xmp_core/public/include/client-glue/WXMPMeta.hpp b/source/lib/xmp_core/public/include/client-glue/WXMPMeta.hpp new file mode 100644 index 0000000..361ad9d --- /dev/null +++ b/source/lib/xmp_core/public/include/client-glue/WXMPMeta.hpp @@ -0,0 +1,621 @@ +#if ! __WXMPMeta_hpp__ +#define __WXMPMeta_hpp__ 1 + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "client-glue/WXMP_Common.hpp" + +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= + +static XMP_Bool WrapErrorNotify ( XMPMeta_ErrorCallbackProc proc, void * context, + XMP_ErrorSeverity severity, XMP_Int32 cause, XMP_StringPtr message ) +{ + bool ok; + try { + ok = (*proc) ( context, severity, cause, message ); + } catch ( ... ) { + ok = false; + } + return ConvertBoolToXMP_Bool( ok ); +} + +// ================================================================================================= + +#define zXMPMeta_GetVersionInfo_1(info) \ + WXMPMeta_GetVersionInfo_1 ( info /* no wResult */ ) + +#define zXMPMeta_Initialize_1() \ + WXMPMeta_Initialize_1 ( &wResult ) +#define zXMPMeta_Terminate_1() \ + WXMPMeta_Terminate_1 ( /* no wResult */ ) + +#define zXMPMeta_CTor_1() \ + WXMPMeta_CTor_1 ( &wResult ) + +#define zXMPMeta_GetGlobalOptions_1() \ + WXMPMeta_GetGlobalOptions_1 ( &wResult ) + +#define zXMPMeta_SetGlobalOptions_1(options) \ + WXMPMeta_SetGlobalOptions_1 ( options, &wResult ) + +#define zXMPMeta_DumpNamespaces_1(outProc,refCon) \ + WXMPMeta_DumpNamespaces_1 ( outProc, refCon, &wResult ) + +#define zXMPMeta_RegisterNamespace_1(namespaceURI,suggestedPrefix,actualPrefix,SetClientString) \ + WXMPMeta_RegisterNamespace_1 ( namespaceURI, suggestedPrefix, actualPrefix, SetClientString, &wResult ) + +#define zXMPMeta_GetNamespacePrefix_1(namespaceURI,namespacePrefix,SetClientString) \ + WXMPMeta_GetNamespacePrefix_1 ( namespaceURI, namespacePrefix, SetClientString, &wResult ) + +#define zXMPMeta_GetNamespaceURI_1(namespacePrefix,namespaceURI,SetClientString) \ + WXMPMeta_GetNamespaceURI_1 ( namespacePrefix, namespaceURI, SetClientString, &wResult ) + +#define zXMPMeta_DeleteNamespace_1(namespaceURI) \ + WXMPMeta_DeleteNamespace_1 ( namespaceURI, &wResult ) + +#define zXMPMeta_GetProperty_1(schemaNS,propName,propValue,options,SetClientString) \ + WXMPMeta_GetProperty_1 ( this->xmpRef, schemaNS, propName, propValue, options, SetClientString, &wResult ) + +#define zXMPMeta_GetArrayItem_1(schemaNS,arrayName,itemIndex,itemValue,options,SetClientString) \ + WXMPMeta_GetArrayItem_1 ( this->xmpRef, schemaNS, arrayName, itemIndex, itemValue, options, SetClientString, &wResult ) + +#define zXMPMeta_GetStructField_1(schemaNS,structName,fieldNS,fieldName,fieldValue,options,SetClientString) \ + WXMPMeta_GetStructField_1 ( this->xmpRef, schemaNS, structName, fieldNS, fieldName, fieldValue, options, SetClientString, &wResult ) + +#define zXMPMeta_GetQualifier_1(schemaNS,propName,qualNS,qualName,qualValue,options,SetClientString) \ + WXMPMeta_GetQualifier_1 ( this->xmpRef, schemaNS, propName, qualNS, qualName, qualValue, options, SetClientString, &wResult ) + +#define zXMPMeta_SetProperty_1(schemaNS,propName,propValue,options) \ + WXMPMeta_SetProperty_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_SetArrayItem_1(schemaNS,arrayName,itemIndex,itemValue,options) \ + WXMPMeta_SetArrayItem_1 ( this->xmpRef, schemaNS, arrayName, itemIndex, itemValue, options, &wResult ) + +#define zXMPMeta_AppendArrayItem_1(schemaNS,arrayName,arrayOptions,itemValue,options) \ + WXMPMeta_AppendArrayItem_1 ( this->xmpRef, schemaNS, arrayName, arrayOptions, itemValue, options, &wResult ) + +#define zXMPMeta_SetStructField_1(schemaNS,structName,fieldNS,fieldName,fieldValue,options) \ + WXMPMeta_SetStructField_1 ( this->xmpRef, schemaNS, structName, fieldNS, fieldName, fieldValue, options, &wResult ) + +#define zXMPMeta_SetQualifier_1(schemaNS,propName,qualNS,qualName,qualValue,options) \ + WXMPMeta_SetQualifier_1 ( this->xmpRef, schemaNS, propName, qualNS, qualName, qualValue, options, &wResult ) + +#define zXMPMeta_DeleteProperty_1(schemaNS,propName) \ + WXMPMeta_DeleteProperty_1 ( this->xmpRef, schemaNS, propName, &wResult ) + +#define zXMPMeta_DeleteArrayItem_1(schemaNS,arrayName,itemIndex) \ + WXMPMeta_DeleteArrayItem_1 ( this->xmpRef, schemaNS, arrayName, itemIndex, &wResult ) + +#define zXMPMeta_DeleteStructField_1(schemaNS,structName,fieldNS,fieldName) \ + WXMPMeta_DeleteStructField_1 ( this->xmpRef, schemaNS, structName, fieldNS, fieldName, &wResult ) + +#define zXMPMeta_DeleteQualifier_1(schemaNS,propName,qualNS,qualName) \ + WXMPMeta_DeleteQualifier_1 ( this->xmpRef, schemaNS, propName, qualNS, qualName, &wResult ) + +#define zXMPMeta_DoesPropertyExist_1(schemaNS,propName) \ + WXMPMeta_DoesPropertyExist_1 ( this->xmpRef, schemaNS, propName, &wResult ) + +#define zXMPMeta_DoesArrayItemExist_1(schemaNS,arrayName,itemIndex) \ + WXMPMeta_DoesArrayItemExist_1 ( this->xmpRef, schemaNS, arrayName, itemIndex, &wResult ) + +#define zXMPMeta_DoesStructFieldExist_1(schemaNS,structName,fieldNS,fieldName) \ + WXMPMeta_DoesStructFieldExist_1 ( this->xmpRef, schemaNS, structName, fieldNS, fieldName, &wResult ) + +#define zXMPMeta_DoesQualifierExist_1(schemaNS,propName,qualNS,qualName) \ + WXMPMeta_DoesQualifierExist_1 ( this->xmpRef, schemaNS, propName, qualNS, qualName, &wResult ) + +#define zXMPMeta_GetLocalizedText_1(schemaNS,altTextName,genericLang,specificLang,clientLang,clientValue,options,SetClientString) \ + WXMPMeta_GetLocalizedText_1 ( this->xmpRef, schemaNS, altTextName, genericLang, specificLang, clientLang, clientValue, options, SetClientString, &wResult ) + +#define zXMPMeta_SetLocalizedText_1(schemaNS,altTextName,genericLang,specificLang,itemValue,options) \ + WXMPMeta_SetLocalizedText_1 ( this->xmpRef, schemaNS, altTextName, genericLang, specificLang, itemValue, options, &wResult ) + +#define zXMPMeta_DeleteLocalizedText_1(schemaNS,altTextName,genericLang,specificLang) \ + WXMPMeta_DeleteLocalizedText_1 ( this->xmpRef, schemaNS, altTextName, genericLang, specificLang, &wResult ) +#define zXMPMeta_GetProperty_Bool_1(schemaNS,propName,propValue,options) \ + WXMPMeta_GetProperty_Bool_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_GetProperty_Int_1(schemaNS,propName,propValue,options) \ + WXMPMeta_GetProperty_Int_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_GetProperty_Int64_1(schemaNS,propName,propValue,options) \ + WXMPMeta_GetProperty_Int64_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_GetProperty_Float_1(schemaNS,propName,propValue,options) \ + WXMPMeta_GetProperty_Float_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_GetProperty_Date_1(schemaNS,propName,propValue,options) \ + WXMPMeta_GetProperty_Date_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_SetProperty_Bool_1(schemaNS,propName,propValue,options) \ + WXMPMeta_SetProperty_Bool_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_SetProperty_Int_1(schemaNS,propName,propValue,options) \ + WXMPMeta_SetProperty_Int_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_SetProperty_Int64_1(schemaNS,propName,propValue,options) \ + WXMPMeta_SetProperty_Int64_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_SetProperty_Float_1(schemaNS,propName,propValue,options) \ + WXMPMeta_SetProperty_Float_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_SetProperty_Date_1(schemaNS,propName,propValue,options) \ + WXMPMeta_SetProperty_Date_1 ( this->xmpRef, schemaNS, propName, propValue, options, &wResult ) + +#define zXMPMeta_GetObjectName_1(objName,SetClientString) \ + WXMPMeta_GetObjectName_1 ( this->xmpRef, objName, SetClientString, &wResult ) + +#define zXMPMeta_SetObjectName_1(name) \ + WXMPMeta_SetObjectName_1 ( this->xmpRef, name, &wResult ) + +#define zXMPMeta_GetObjectOptions_1() \ + WXMPMeta_GetObjectOptions_1 ( this->xmpRef, &wResult ) + +#define zXMPMeta_SetObjectOptions_1(options) \ + WXMPMeta_SetObjectOptions_1 ( this->xmpRef, options, &wResult ) + +#define zXMPMeta_Sort_1() \ + WXMPMeta_Sort_1 ( this->xmpRef, &wResult ) + +#define zXMPMeta_Erase_1() \ + WXMPMeta_Erase_1 ( this->xmpRef, &wResult ) + +#define zXMPMeta_Clone_1(options) \ + WXMPMeta_Clone_1 ( this->xmpRef, options, &wResult ) + +#define zXMPMeta_CountArrayItems_1(schemaNS,arrayName) \ + WXMPMeta_CountArrayItems_1 ( this->xmpRef, schemaNS, arrayName, &wResult ) + +#define zXMPMeta_DumpObject_1(outProc,refCon) \ + WXMPMeta_DumpObject_1 ( this->xmpRef, outProc, refCon, &wResult ) + +#define zXMPMeta_ParseFromBuffer_1(buffer,bufferSize,options) \ + WXMPMeta_ParseFromBuffer_1 ( this->xmpRef, buffer, bufferSize, options, &wResult ) + +#define zXMPMeta_SerializeToBuffer_1(pktString,options,padding,newline,indent,baseIndent,SetClientString) \ + WXMPMeta_SerializeToBuffer_1 ( this->xmpRef, pktString, options, padding, newline, indent, baseIndent, SetClientString, &wResult ) + +#define zXMPMeta_SetDefaultErrorCallback_1(proc,context,limit) \ + WXMPMeta_SetDefaultErrorCallback_1 ( WrapErrorNotify, proc, context, limit, &wResult ) + +#define zXMPMeta_SetErrorCallback_1(proc,context,limit) \ + WXMPMeta_SetErrorCallback_1 ( this->xmpRef, WrapErrorNotify, proc, context, limit, &wResult ) + +#define zXMPMeta_ResetErrorCallbackLimit_1(limit) \ + WXMPMeta_ResetErrorCallbackLimit_1 ( this->xmpRef, limit, &wResult ) + +// ================================================================================================= + +extern void +XMP_PUBLIC WXMPMeta_GetVersionInfo_1 ( XMP_VersionInfo * info ); + +extern void +XMP_PUBLIC WXMPMeta_Initialize_1 ( WXMP_Result * wResult ); +extern void +XMP_PUBLIC WXMPMeta_Terminate_1(); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_CTor_1 ( WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_IncrementRefCount_1 ( XMPMetaRef xmpRef ); + +extern void +XMP_PUBLIC WXMPMeta_DecrementRefCount_1 ( XMPMetaRef xmpRef ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_GetGlobalOptions_1 ( WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetGlobalOptions_1 ( XMP_OptionBits options, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_DumpNamespaces_1 ( XMP_TextOutputProc outProc, + void * refCon, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_RegisterNamespace_1 ( XMP_StringPtr namespaceURI, + XMP_StringPtr suggestedPrefix, + void * actualPrefix, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_GetNamespacePrefix_1 ( XMP_StringPtr namespaceURI, + void * namespacePrefix, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_GetNamespaceURI_1 ( XMP_StringPtr namespacePrefix, + void * namespaceURI, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_DeleteNamespace_1 ( XMP_StringPtr namespaceURI, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_GetProperty_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + void * propValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetArrayItem_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + void * itemValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetStructField_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + void * fieldValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetQualifier_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + void * qualValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_SetProperty_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr propValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetArrayItem_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + XMP_StringPtr itemValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_AppendArrayItem_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits arrayOptions, + XMP_StringPtr itemValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetStructField_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetQualifier_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + XMP_StringPtr qualValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_DeleteProperty_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_DeleteArrayItem_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_DeleteStructField_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_DeleteQualifier_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_DoesPropertyExist_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_DoesArrayItemExist_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_DoesStructFieldExist_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_DoesQualifierExist_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + WXMP_Result * wResult ) /* const */ ; + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_GetLocalizedText_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + void * clientLang, + void * clientValue, + XMP_OptionBits * options, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_SetLocalizedText_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + XMP_StringPtr itemValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_DeleteLocalizedText_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr altTextName, + XMP_StringPtr genericLang, + XMP_StringPtr specificLang, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_GetProperty_Bool_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Bool * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetProperty_Int_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetProperty_Int64_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetProperty_Float_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_GetProperty_Date_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_DateTime * propValue, + XMP_OptionBits * options, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_SetProperty_Bool_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Bool propValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetProperty_Int_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int32 propValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetProperty_Int64_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_Int64 propValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetProperty_Float_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + double propValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetProperty_Date_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + const XMP_DateTime & propValue, + XMP_OptionBits options, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_GetObjectName_1 ( XMPMetaRef xmpRef, + void * objName, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_SetObjectName_1 ( XMPMetaRef xmpRef, + XMP_StringPtr name, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_GetObjectOptions_1 ( XMPMetaRef xmpRef, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_SetObjectOptions_1 ( XMPMetaRef xmpRef, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_Sort_1 ( XMPMetaRef xmpRef, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_Erase_1 ( XMPMetaRef xmpRef, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_Clone_1 ( XMPMetaRef xmpRef, + XMP_OptionBits options, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_CountArrayItems_1 ( XMPMetaRef xmpRef, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + WXMP_Result * wResult ) /* const */ ; + +extern void +XMP_PUBLIC WXMPMeta_DumpObject_1 ( XMPMetaRef xmpRef, + XMP_TextOutputProc outProc, + void * refCon, + WXMP_Result * wResult ) /* const */ ; + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_ParseFromBuffer_1 ( XMPMetaRef xmpRef, + XMP_StringPtr buffer, + XMP_StringLen bufferSize, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SerializeToBuffer_1 ( XMPMetaRef xmpRef, + void * pktString, + XMP_OptionBits options, + XMP_StringLen padding, + XMP_StringPtr newline, + XMP_StringPtr indent, + XMP_Index baseIndent, + SetClientStringProc SetClientString, + WXMP_Result * wResult ) /* const */ ; + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPMeta_SetDefaultErrorCallback_1 ( XMPMeta_ErrorCallbackWrapper wrapperProc, + XMPMeta_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_SetErrorCallback_1 ( XMPMetaRef xmpRef, + XMPMeta_ErrorCallbackWrapper wrapperProc, + XMPMeta_ErrorCallbackProc clientProc, + void * context, + XMP_Uns32 limit, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPMeta_ResetErrorCallbackLimit_1 ( XMPMetaRef xmpRef, + XMP_Uns32 limit, + WXMP_Result * wResult ); + +// ================================================================================================= + +#if __cplusplus +} /* extern "C" */ +#endif + +#endif // __WXMPMeta_hpp__ diff --git a/source/lib/xmp_core/public/include/client-glue/WXMPUtils.hpp b/source/lib/xmp_core/public/include/client-glue/WXMPUtils.hpp new file mode 100644 index 0000000..3c96b83 --- /dev/null +++ b/source/lib/xmp_core/public/include/client-glue/WXMPUtils.hpp @@ -0,0 +1,315 @@ +#if ! __WXMPUtils_hpp__ +#define __WXMPUtils_hpp__ 1 + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#include "client-glue/WXMP_Common.hpp" + +#if __cplusplus +extern "C" { +#endif + +// ================================================================================================= + +#define zXMPUtils_ComposeArrayItemPath_1(schemaNS,arrayName,itemIndex,itemPath,SetClientString) \ + WXMPUtils_ComposeArrayItemPath_1 ( schemaNS, arrayName, itemIndex, itemPath, SetClientString, &wResult ); + +#define zXMPUtils_ComposeStructFieldPath_1(schemaNS,structName,fieldNS,fieldName,fieldPath,SetClientString) \ + WXMPUtils_ComposeStructFieldPath_1 ( schemaNS, structName, fieldNS, fieldName, fieldPath, SetClientString, &wResult ); + +#define zXMPUtils_ComposeQualifierPath_1(schemaNS,propName,qualNS,qualName,qualPath,SetClientString) \ + WXMPUtils_ComposeQualifierPath_1 ( schemaNS, propName, qualNS, qualName, qualPath, SetClientString, &wResult ); + +#define zXMPUtils_ComposeLangSelector_1(schemaNS,arrayName,langName,selPath,SetClientString) \ + WXMPUtils_ComposeLangSelector_1 ( schemaNS, arrayName, langName, selPath, SetClientString, &wResult ); + +#define zXMPUtils_ComposeFieldSelector_1(schemaNS,arrayName,fieldNS,fieldName,fieldValue,selPath,SetClientString) \ + WXMPUtils_ComposeFieldSelector_1 ( schemaNS, arrayName, fieldNS, fieldName, fieldValue, selPath, SetClientString, &wResult ); + +#define zXMPUtils_ConvertFromBool_1(binValue,strValue,SetClientString) \ + WXMPUtils_ConvertFromBool_1 ( binValue, strValue, SetClientString, &wResult ); + +#define zXMPUtils_ConvertFromInt_1(binValue,format,strValue,SetClientString) \ + WXMPUtils_ConvertFromInt_1 ( binValue, format, strValue, SetClientString, &wResult ); + +#define zXMPUtils_ConvertFromInt64_1(binValue,format,strValue,SetClientString) \ + WXMPUtils_ConvertFromInt64_1 ( binValue, format, strValue, SetClientString, &wResult ); + +#define zXMPUtils_ConvertFromFloat_1(binValue,format,strValue,SetClientString) \ + WXMPUtils_ConvertFromFloat_1 ( binValue, format, strValue, SetClientString, &wResult ); + +#define zXMPUtils_ConvertFromDate_1(binValue,strValue,SetClientString) \ + WXMPUtils_ConvertFromDate_1 ( binValue, strValue, SetClientString, &wResult ); + +#define zXMPUtils_ConvertToBool_1(strValue) \ + WXMPUtils_ConvertToBool_1 ( strValue, &wResult ); + +#define zXMPUtils_ConvertToInt_1(strValue) \ + WXMPUtils_ConvertToInt_1 ( strValue, &wResult ); + +#define zXMPUtils_ConvertToInt64_1(strValue) \ + WXMPUtils_ConvertToInt64_1 ( strValue, &wResult ); + +#define zXMPUtils_ConvertToFloat_1(strValue) \ + WXMPUtils_ConvertToFloat_1 ( strValue, &wResult ); + +#define zXMPUtils_ConvertToDate_1(strValue,binValue) \ + WXMPUtils_ConvertToDate_1 ( strValue, binValue, &wResult ); + +#define zXMPUtils_CurrentDateTime_1(time) \ + WXMPUtils_CurrentDateTime_1 ( time, &wResult ); + +#define zXMPUtils_SetTimeZone_1(time) \ + WXMPUtils_SetTimeZone_1 ( time, &wResult ); + +#define zXMPUtils_ConvertToUTCTime_1(time) \ + WXMPUtils_ConvertToUTCTime_1 ( time, &wResult ); + +#define zXMPUtils_ConvertToLocalTime_1(time) \ + WXMPUtils_ConvertToLocalTime_1 ( time, &wResult ); + +#define zXMPUtils_CompareDateTime_1(left,right) \ + WXMPUtils_CompareDateTime_1 ( left, right, &wResult ); + +#define zXMPUtils_EncodeToBase64_1(rawStr,rawLen,encodedStr,SetClientString) \ + WXMPUtils_EncodeToBase64_1 ( rawStr, rawLen, encodedStr, SetClientString, &wResult ); + +#define zXMPUtils_DecodeFromBase64_1(encodedStr,encodedLen,rawStr,SetClientString) \ + WXMPUtils_DecodeFromBase64_1 ( encodedStr, encodedLen, rawStr, SetClientString, &wResult ); + +#define zXMPUtils_PackageForJPEG_1(xmpObj,stdStr,extStr,digestStr,SetClientString) \ + WXMPUtils_PackageForJPEG_1 ( xmpObj, stdStr, extStr, digestStr, SetClientString, &wResult ); + +#define zXMPUtils_MergeFromJPEG_1(fullXMP,extendedXMP) \ + WXMPUtils_MergeFromJPEG_1 ( fullXMP, extendedXMP, &wResult ); + +#define zXMPUtils_CatenateArrayItems_1(xmpObj,schemaNS,arrayName,separator,quotes,options,catedStr,SetClientString) \ + WXMPUtils_CatenateArrayItems_1 ( xmpObj, schemaNS, arrayName, separator, quotes, options, catedStr, SetClientString, &wResult ); + +#define zXMPUtils_SeparateArrayItems_1(xmpObj,schemaNS,arrayName,options,catedStr) \ + WXMPUtils_SeparateArrayItems_1 ( xmpObj, schemaNS, arrayName, options, catedStr, &wResult ); + +#define zXMPUtils_ApplyTemplate_1(workingXMP,templateXMP,actions) \ + WXMPUtils_ApplyTemplate_1 ( workingXMP, templateXMP, actions, &wResult ); + +#define zXMPUtils_RemoveProperties_1(xmpObj,schemaNS,propName,options) \ + WXMPUtils_RemoveProperties_1 ( xmpObj, schemaNS, propName, options, &wResult ); + +#define zXMPUtils_DuplicateSubtree_1(source,dest,sourceNS,sourceRoot,destNS,destRoot,options) \ + WXMPUtils_DuplicateSubtree_1 ( source, dest, sourceNS, sourceRoot, destNS, destRoot, options, &wResult ); + +// ================================================================================================= + +extern void +XMP_PUBLIC WXMPUtils_ComposeArrayItemPath_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_Index itemIndex, + void * itemPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ComposeStructFieldPath_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr structName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + void * fieldPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ComposeQualifierPath_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_StringPtr qualNS, + XMP_StringPtr qualName, + void * qualPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ComposeLangSelector_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr langName, + void * selPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ComposeFieldSelector_1 ( XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr fieldNS, + XMP_StringPtr fieldName, + XMP_StringPtr fieldValue, + void * selPath, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPUtils_ConvertFromBool_1 ( XMP_Bool binValue, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertFromInt_1 ( XMP_Int32 binValue, + XMP_StringPtr format, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertFromInt64_1 ( XMP_Int64 binValue, + XMP_StringPtr format, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertFromFloat_1 ( double binValue, + XMP_StringPtr format, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertFromDate_1 ( const XMP_DateTime & binValue, + void * strValue, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPUtils_ConvertToBool_1 ( XMP_StringPtr strValue, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertToInt_1 ( XMP_StringPtr strValue, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertToInt64_1 ( XMP_StringPtr strValue, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertToFloat_1 ( XMP_StringPtr strValue, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertToDate_1 ( XMP_StringPtr strValue, + XMP_DateTime * binValue, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPUtils_CurrentDateTime_1 ( XMP_DateTime * time, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_SetTimeZone_1 ( XMP_DateTime * time, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertToUTCTime_1 ( XMP_DateTime * time, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ConvertToLocalTime_1 ( XMP_DateTime * time, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_CompareDateTime_1 ( const XMP_DateTime & left, + const XMP_DateTime & right, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPUtils_EncodeToBase64_1 ( XMP_StringPtr rawStr, + XMP_StringLen rawLen, + void * encodedStr, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_DecodeFromBase64_1 ( XMP_StringPtr encodedStr, + XMP_StringLen encodedLen, + void * rawStr, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPUtils_PackageForJPEG_1 ( XMPMetaRef xmpObj, + void * stdStr, + void * extStr, + void * digestStr, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_MergeFromJPEG_1 ( XMPMetaRef fullXMP, + XMPMetaRef extendedXMP, + WXMP_Result * wResult ); + +// ------------------------------------------------------------------------------------------------- + +extern void +XMP_PUBLIC WXMPUtils_CatenateArrayItems_1 ( XMPMetaRef xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_StringPtr separator, + XMP_StringPtr quotes, + XMP_OptionBits options, + void * catedStr, + SetClientStringProc SetClientString, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_SeparateArrayItems_1 ( XMPMetaRef xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr arrayName, + XMP_OptionBits options, + XMP_StringPtr catedStr, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_ApplyTemplate_1 ( XMPMetaRef workingXMP, + XMPMetaRef templateXMP, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_RemoveProperties_1 ( XMPMetaRef xmpObj, + XMP_StringPtr schemaNS, + XMP_StringPtr propName, + XMP_OptionBits options, + WXMP_Result * wResult ); + +extern void +XMP_PUBLIC WXMPUtils_DuplicateSubtree_1 ( XMPMetaRef source, + XMPMetaRef dest, + XMP_StringPtr sourceNS, + XMP_StringPtr sourceRoot, + XMP_StringPtr destNS, + XMP_StringPtr destRoot, + XMP_OptionBits options, + WXMP_Result * wResult ); + +// ================================================================================================= + +#if __cplusplus +} /* extern "C" */ +#endif + +#endif // __WXMPUtils_hpp__ diff --git a/source/lib/xmp_core/public/include/client-glue/WXMP_Common.hpp b/source/lib/xmp_core/public/include/client-glue/WXMP_Common.hpp new file mode 100644 index 0000000..97fb9fc --- /dev/null +++ b/source/lib/xmp_core/public/include/client-glue/WXMP_Common.hpp @@ -0,0 +1,128 @@ +#if ! __WXMP_Common_hpp__ +#define __WXMP_Common_hpp__ 1 + +// ================================================================================================= +// Copyright 2002 Adobe Systems Incorporated +// All Rights Reserved. +// +// NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the terms +// of the Adobe license agreement accompanying it. +// ================================================================================================= + +#ifndef XMP_Inline + #if TXMP_EXPAND_INLINE + #define XMP_Inline inline + #else + #define XMP_Inline /* not inline */ + #endif +#endif + +#define XMP_CTorDTorIntro(Class) template XMP_Inline Class +#define XMP_MethodIntro(Class,ResultType) template XMP_Inline ResultType Class + +typedef void (* SetClientStringProc) ( void * clientPtr, XMP_StringPtr valuePtr, XMP_StringLen valueLen ); +typedef void (* SetClientStringVectorProc) ( void * clientPtr, XMP_StringPtr * arrayPtr, XMP_Uns32 stringCount ); + +struct WXMP_Result { + XMP_StringPtr errMessage; + void * ptrResult; + double floatResult; + XMP_Uns64 int64Result; + XMP_Uns32 int32Result; + WXMP_Result() : errMessage(0),ptrResult(NULL),floatResult(0),int64Result(0),int32Result(0){}; +}; + +#if __cplusplus +extern "C" { +#endif + +#define PropagateException(res) \ + if ( res.errMessage != 0 ) throw XMP_Error ( res.int32Result, res.errMessage ); + +#ifndef XMP_TraceClientCalls + #define XMP_TraceClientCalls 0 + #define XMP_TraceClientCallsToFile 0 +#endif + +#if ! XMP_TraceClientCalls + #define InvokeCheck(WCallProto) \ + WXMP_Result wResult; \ + WCallProto; \ + PropagateException ( wResult ) +#else + extern FILE * xmpClientLog; + #define InvokeCheck(WCallProto) \ + WXMP_Result wResult; \ + fprintf ( xmpClientLog, "WXMP calling: %s\n", #WCallProto ); fflush ( xmpClientLog ); \ + WCallProto; \ + if ( wResult.errMessage == 0 ) { \ + fprintf ( xmpClientLog, "WXMP back, no error\n" ); fflush ( xmpClientLog ); \ + } else { \ + fprintf ( xmpClientLog, "WXMP back, error: %s\n", wResult.errMessage ); fflush ( xmpClientLog ); \ + } \ + PropagateException ( wResult ) +#endif + +// ================================================================================================= + +#define WrapNoCheckVoid(WCallProto) \ + WCallProto; + +#define WrapCheckVoid(WCallProto) \ + InvokeCheck(WCallProto); + +#define WrapCheckMetaRef(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMPMetaRef result = XMPMetaRef(wResult.ptrResult) + +#define WrapCheckIterRef(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMPIteratorRef result = XMPIteratorRef(wResult.ptrResult) + +#define WrapCheckDocOpsRef(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMPDocOpsRef result = XMPDocOpsRef(wResult.ptrResult) + +#define WrapCheckBool(result,WCallProto) \ + InvokeCheck(WCallProto); \ + bool result = bool(wResult.int32Result) + +#define WrapCheckTriState(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_TriState result = XMP_TriState(wResult.int32Result) + +#define WrapCheckOptions(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_OptionBits result = XMP_OptionBits(wResult.int32Result) + +#define WrapCheckStatus(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_Status result = XMP_Status(wResult.int32Result) + +#define WrapCheckIndex(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_Index result = XMP_Index(wResult.int32Result) + +#define WrapCheckInt32(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_Int32 result = wResult.int32Result + +#define WrapCheckInt64(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_Int64 result = wResult.int64Result + +#define WrapCheckFloat(result,WCallProto) \ + InvokeCheck(WCallProto); \ + double result = wResult.floatResult + +#define WrapCheckFormat(result,WCallProto) \ + InvokeCheck(WCallProto); \ + XMP_FileFormat result = wResult.int32Result + +// ================================================================================================= + +#if __cplusplus +} // extern "C" +#endif + +#endif // __WXMP_Common_hpp__