Mercurial > hg > orthanc-stone
changeset 0:351ab0da0150
initial commit
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/AUTHORS Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,13 @@ +Stone of Orthanc +================ + + +Authors +------- + +* Sebastien Jodogne <s.jodogne@gmail.com> + Department of Medical Physics + University Hospital of Liege + Belgium + + Overall design and lead developer.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CMakeLists.txt Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,20 @@ +cmake_minimum_required(VERSION 2.8) +project(OrthancStone) + +include(Resources/CMake/OrthancStone.cmake) + +add_library(OrthancStone ${ORTHANC_STONE_SOURCES}) + +macro(BuildSample Target Sample) + add_executable(${Target} Samples/SampleMainSdl.cpp) + set_target_properties(${Target} PROPERTIES COMPILE_DEFINITIONS ORTHANC_STONE_SAMPLE=${Sample}) + target_link_libraries(${Target} OrthancStone) +endmacro() + +BuildSample(OrthancStoneEmpty 1) +BuildSample(OrthancStoneTestPattern 2) +BuildSample(OrthancStoneSingleFrame 3) +BuildSample(OrthancStoneSingleVolume 4) +BuildSample(OrthancStoneBasicPetCtFusion 5) +BuildSample(OrthancStoneSynchronizedSeries 6) +BuildSample(OrthancStoneLayoutPetCtFusion 7)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/COPYING Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + <program> Copyright (C) <year> <name of author> + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +<http://www.gnu.org/licenses/>. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +<http://www.gnu.org/philosophy/why-not-lgpl.html>.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Applications/BasicApplicationContext.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,146 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "BasicApplicationContext.h" + +#include "../Toolbox/OrthancSeriesLoader.h" +#include "../Volumes/VolumeImageSimplePolicy.h" +#include "../Volumes/VolumeImageProgressivePolicy.h" + +namespace OrthancStone +{ + BasicApplicationContext::BasicApplicationContext(IOrthancConnection& orthanc) : + orthanc_(orthanc) + { + } + + + BasicApplicationContext::~BasicApplicationContext() + { + for (Interactors::iterator it = interactors_.begin(); it != interactors_.end(); ++it) + { + assert(*it != NULL); + delete *it; + } + + for (Volumes::iterator it = volumes_.begin(); it != volumes_.end(); ++it) + { + assert(*it != NULL); + delete *it; + } + + for (StructureSets::iterator it = structureSets_.begin(); it != structureSets_.end(); ++it) + { + assert(*it != NULL); + delete *it; + } + } + + + IWidget& BasicApplicationContext::SetCentralWidget(IWidget* widget) // Takes ownership + { + viewport_.SetCentralWidget(widget); + return *widget; + } + + + VolumeImage& BasicApplicationContext::AddSeriesVolume(const std::string& series, + bool isProgressiveDownload, + size_t downloadThreadCount) + { + std::auto_ptr<VolumeImage> volume(new VolumeImage(new OrthancSeriesLoader(orthanc_, series))); + + if (isProgressiveDownload) + { + volume->SetDownloadPolicy(new VolumeImageProgressivePolicy); + } + else + { + volume->SetDownloadPolicy(new VolumeImageSimplePolicy); + } + + volume->SetThreadCount(downloadThreadCount); + + VolumeImage& result = *volume; + volumes_.push_back(volume.release()); + + return result; + } + + + DicomStructureSet& BasicApplicationContext::AddStructureSet(const std::string& instance) + { + std::auto_ptr<DicomStructureSet> structureSet(new DicomStructureSet(orthanc_, instance)); + + DicomStructureSet& result = *structureSet; + structureSets_.push_back(structureSet.release()); + + return result; + } + + + IWorldSceneInteractor& BasicApplicationContext::AddInteractor(IWorldSceneInteractor* interactor) + { + if (interactor == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + interactors_.push_back(interactor); + + return *interactor; + } + + + void BasicApplicationContext::Start() + { + for (Volumes::iterator it = volumes_.begin(); it != volumes_.end(); ++it) + { + assert(*it != NULL); + (*it)->Start(); + } + + viewport_.Start(); + } + + + void BasicApplicationContext::Stop() + { + viewport_.Stop(); + + for (Volumes::iterator it = volumes_.begin(); it != volumes_.end(); ++it) + { + assert(*it != NULL); + (*it)->Stop(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Applications/BasicApplicationContext.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,86 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Volumes/VolumeImage.h" +#include "../Viewport/WidgetViewport.h" +#include "../Widgets/IWorldSceneInteractor.h" +#include "../Toolbox/DicomStructureSet.h" + +#include <list> + +namespace OrthancStone +{ + class BasicApplicationContext : public boost::noncopyable + { + private: + typedef std::list<ISliceableVolume*> Volumes; + typedef std::list<IWorldSceneInteractor*> Interactors; + typedef std::list<DicomStructureSet*> StructureSets; + + IOrthancConnection& orthanc_; + WidgetViewport viewport_; + Volumes volumes_; + Interactors interactors_; + StructureSets structureSets_; + + public: + BasicApplicationContext(IOrthancConnection& orthanc); + + ~BasicApplicationContext(); + + IWidget& SetCentralWidget(IWidget* widget); // Takes ownership + + IViewport& GetViewport() + { + return viewport_; + } + + IOrthancConnection& GetOrthancConnection() + { + return orthanc_; + } + + VolumeImage& AddSeriesVolume(const std::string& series, + bool isProgressiveDownload, + size_t downloadThreadCount); + + DicomStructureSet& AddStructureSet(const std::string& instance); + + IWorldSceneInteractor& AddInteractor(IWorldSceneInteractor* interactor); + + void Start(); + + void Stop(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Applications/IBasicApplication.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,284 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "IBasicApplication.h" + +#include "../Orthanc/Core/Logging.h" +#include "../Orthanc/Core/HttpClient.h" +#include "../Messaging/CurlOrthancConnection.h" +#include "Sdl/SdlEngine.h" + +namespace OrthancStone +{ + // Anonymous namespace to avoid clashes against other compilation modules + namespace + { + class LogStatusBar : public IStatusBar + { + public: + virtual void ClearMessage() + { + } + + virtual void SetMessage(const std::string& message) + { + LOG(WARNING) << message; + } + }; + } + + +#if ORTHANC_ENABLE_SDL == 1 + static void DeclareSdlCommandLineOptions(boost::program_options::options_description& options) + { + // Declare the supported parameters + boost::program_options::options_description generic("Generic options"); + generic.add_options() + ("help", "Display this help and exit") + ("verbose", "Be verbose in logs") + ("orthanc", boost::program_options::value<std::string>()->default_value("http://localhost:8042/"), + "URL to the Orthanc server") + ("username", "Username for the Orthanc server") + ("password", "Password for the Orthanc server") + ("https-verify", boost::program_options::value<bool>()->default_value(true), "Check HTTPS certificates") + ; + + options.add(generic); + + boost::program_options::options_description sdl("SDL options"); + sdl.add_options() + ("width", boost::program_options::value<int>()->default_value(1024), "Initial width of the SDL window") + ("height", boost::program_options::value<int>()->default_value(768), "Initial height of the SDL window") + ("opengl", boost::program_options::value<bool>()->default_value(true), "Enable OpenGL in SDL") + ; + + options.add(sdl); + } + + + int IBasicApplication::ExecuteWithSdl(IBasicApplication& application, + int argc, + char* argv[]) + { + /****************************************************************** + * Initialize all the subcomponents of Orthanc Stone + ******************************************************************/ + + Orthanc::Logging::Initialize(); + Orthanc::HttpClient::InitializeOpenSsl(); + Orthanc::HttpClient::GlobalInitialize(); + SdlEngine::GlobalInitialize(); + + + /****************************************************************** + * Declare and parse the command-line options of the application + ******************************************************************/ + + boost::program_options::options_description options; + DeclareSdlCommandLineOptions(options); + application.DeclareCommandLineOptions(options); + + boost::program_options::variables_map parameters; + bool error = false; + + try + { + boost::program_options::store(boost::program_options::command_line_parser(argc, argv). + options(options).run(), parameters); + boost::program_options::notify(parameters); + } + catch (boost::program_options::error& e) + { + LOG(ERROR) << "Error while parsing the command-line arguments: " << e.what(); + error = true; + } + + + /****************************************************************** + * Configure the application with the command-line parameters + ******************************************************************/ + + if (error || parameters.count("help")) + { + std::cout << std::endl + << "Usage: " << argv[0] << " [OPTION]..." + << std::endl + << "Orthanc, lightweight, RESTful DICOM server for healthcare and medical research." + << std::endl << std::endl + << "Demonstration application of Orthanc Stone using SDL." + << std::endl; + + std::cout << options << "\n"; + return error ? -1 : 0; + } + + if (parameters.count("https-verify") && + !parameters["https-verify"].as<bool>()) + { + LOG(WARNING) << "Turning off verification of HTTPS certificates (unsafe)"; + Orthanc::HttpClient::ConfigureSsl(false, ""); + } + + if (parameters.count("verbose")) + { + Orthanc::Logging::EnableInfoLevel(true); + } + + if (!parameters.count("width") || + !parameters.count("height") || + !parameters.count("opengl")) + { + LOG(ERROR) << "Parameter \"width\", \"height\" or \"opengl\" is missing"; + return -1; + } + + int w = parameters["width"].as<int>(); + int h = parameters["height"].as<int>(); + if (w <= 0 || h <= 0) + { + LOG(ERROR) << "Parameters \"width\" and \"height\" must be positive"; + return -1; + } + + unsigned int width = static_cast<unsigned int>(w); + unsigned int height = static_cast<unsigned int>(h); + LOG(WARNING) << "Initial display size: " << width << "x" << height; + + bool opengl = parameters["opengl"].as<bool>(); + if (opengl) + { + LOG(WARNING) << "OpenGL is enabled, disable it with option \"--opengl=off\" if the application crashes"; + } + else + { + LOG(WARNING) << "OpenGL is disabled, enable it with option \"--opengl=on\" for best performance"; + } + + bool success = true; + try + { + /**************************************************************** + * Initialize the connection to the Orthanc server + ****************************************************************/ + + Orthanc::WebServiceParameters webService; + + if (parameters.count("orthanc")) + { + webService.SetUrl(parameters["orthanc"].as<std::string>()); + } + + if (parameters.count("username")) + { + webService.SetUsername(parameters["username"].as<std::string>()); + } + + if (parameters.count("password")) + { + webService.SetPassword(parameters["password"].as<std::string>()); + } + + LOG(WARNING) << "URL to the Orthanc REST API: " << webService.GetUrl(); + CurlOrthancConnection orthanc(webService); + + if (!MessagingToolbox::CheckOrthancVersion(orthanc)) + { + LOG(ERROR) << "Your version of Orthanc is incompatible with Orthanc Stone, please upgrade"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + + /**************************************************************** + * Initialize the application + ****************************************************************/ + + LogStatusBar statusBar; + BasicApplicationContext context(orthanc); + + application.Initialize(context, statusBar, parameters); + context.GetViewport().SetStatusBar(statusBar); + + std::string title = application.GetTitle(); + if (title.empty()) + { + title = "Stone of Orthanc"; + } + + context.Start(); + + { + /************************************************************** + * Run the application inside a SDL window + **************************************************************/ + + LOG(WARNING) << "Starting the application"; + + SdlWindow window(title.c_str(), width, height, opengl); + SdlEngine sdl(window, context.GetViewport()); + + sdl.Run(); + + LOG(WARNING) << "Stopping the application"; + } + + + /**************************************************************** + * Finalize the application + ****************************************************************/ + + context.Stop(); + + LOG(WARNING) << "The application has stopped"; + + context.GetViewport().ResetStatusBar(); + application.Finalize(); + } + catch (Orthanc::OrthancException& e) + { + LOG(ERROR) << "EXCEPTION: " << e.What(); + success = false; + } + + + /****************************************************************** + * Finalize all the subcomponents of Orthanc Stone + ******************************************************************/ + + SdlEngine::GlobalFinalize(); + Orthanc::HttpClient::GlobalFinalize(); + Orthanc::HttpClient::FinalizeOpenSsl(); + + return (success ? 0 : -1); + } +#endif + +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Applications/IBasicApplication.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,68 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "BasicApplicationContext.h" + +#include <boost/program_options.hpp> + +#if ORTHANC_ENABLE_SDL == 1 +# include <SDL.h> // Necessary to avoid undefined reference to `SDL_main' +#endif + +namespace OrthancStone +{ + class IBasicApplication : public boost::noncopyable + { + public: + virtual ~IBasicApplication() + { + } + + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) = 0; + + virtual std::string GetTitle() const = 0; + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) = 0; + + virtual void Finalize() = 0; + +#if ORTHANC_ENABLE_SDL == 1 + static int ExecuteWithSdl(IBasicApplication& application, + int argc, + char* argv[]); +#endif + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Applications/Sdl/SdlBuffering.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,145 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "SdlBuffering.h" + +#if ORTHANC_ENABLE_SDL == 1 + +#include "../../Orthanc/Core/Logging.h" +#include "../../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + SdlBuffering::SdlBuffering() : + sdlSurface_(NULL), + pendingFrame_(false) + { + } + + + SdlBuffering::~SdlBuffering() + { + if (sdlSurface_) + { + SDL_FreeSurface(sdlSurface_); + } + } + + + void SdlBuffering::SetSize(unsigned int width, + unsigned int height, + IViewport& viewport) + { + boost::mutex::scoped_lock lock(mutex_); + + viewport.SetSize(width, height); + + if (offscreenSurface_.get() == NULL || + offscreenSurface_->GetWidth() != width || + offscreenSurface_->GetHeight() != height) + { + offscreenSurface_.reset(new CairoSurface(width, height)); + } + + if (onscreenSurface_.get() == NULL || + onscreenSurface_->GetWidth() != width || + onscreenSurface_->GetHeight() != height) + { + onscreenSurface_.reset(new CairoSurface(width, height)); + + // TODO Big endian? + static const uint32_t rmask = 0x00ff0000; + static const uint32_t gmask = 0x0000ff00; + static const uint32_t bmask = 0x000000ff; + + if (sdlSurface_) + { + SDL_FreeSurface(sdlSurface_); + } + + sdlSurface_ = SDL_CreateRGBSurfaceFrom(onscreenSurface_->GetBuffer(), width, height, 32, + onscreenSurface_->GetPitch(), rmask, gmask, bmask, 0); + if (!sdlSurface_) + { + LOG(ERROR) << "Cannot create a SDL surface from a Cairo surface"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + pendingFrame_ = false; + } + + + bool SdlBuffering::RenderOffscreen(IViewport& viewport) + { + boost::mutex::scoped_lock lock(mutex_); + + if (offscreenSurface_.get() == NULL) + { + return false; + } + + Orthanc::ImageAccessor target = offscreenSurface_->GetAccessor(); + + if (viewport.Render(target) && + !pendingFrame_) + { + pendingFrame_ = true; + return true; + } + else + { + return false; + } + } + + + void SdlBuffering::SwapToScreen(SdlWindow& window) + { + if (!pendingFrame_ || + offscreenSurface_.get() == NULL || + onscreenSurface_.get() == NULL) + { + return; + } + + { + boost::mutex::scoped_lock lock(mutex_); + onscreenSurface_->Copy(*offscreenSurface_); + } + + window.Render(sdlSurface_); + pendingFrame_ = false; + } +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Applications/Sdl/SdlBuffering.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,71 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if ORTHANC_ENABLE_SDL == 1 + +#include "SdlWindow.h" +#include "../../Viewport/CairoSurface.h" +#include "../../Viewport/IViewport.h" + +#include <boost/thread/mutex.hpp> + +namespace OrthancStone +{ + class SdlBuffering : public boost::noncopyable + { + private: + boost::mutex mutex_; + std::auto_ptr<CairoSurface> offscreenSurface_; + std::auto_ptr<CairoSurface> onscreenSurface_; + SDL_Surface* sdlSurface_; + bool pendingFrame_; + + public: + SdlBuffering(); + + ~SdlBuffering(); + + void SetSize(unsigned int width, + unsigned int height, + IViewport& viewport); + + // Returns "true" if a new refresh of the display should be + // triggered afterwards + bool RenderOffscreen(IViewport& viewport); + + void SwapToScreen(SdlWindow& window); + }; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Applications/Sdl/SdlEngine.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,330 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "SdlEngine.h" + +#if ORTHANC_ENABLE_SDL == 1 + +#include "../../Orthanc/Core/Logging.h" + +#include <SDL.h> + +namespace OrthancStone +{ + void SdlEngine::RenderFrame() + { + if (!viewportChanged_) + { + return; + } + + viewportChanged_ = false; + + if (buffering_.RenderOffscreen(viewport_)) + { + // Do not notify twice when a new frame was rendered, to avoid + // spoiling the SDL event queue + SDL_Event event; + SDL_memset(&event, 0, sizeof(event)); + event.type = refreshEvent_; + event.user.code = 0; + event.user.data1 = 0; + event.user.data2 = 0; + SDL_PushEvent(&event); + } + } + + + void SdlEngine::RenderThread(SdlEngine* that) + { + for (;;) + { + that->renderFrame_.Wait(); + + if (that->continue_) + { + that->RenderFrame(); + } + else + { + return; + } + } + } + + + KeyboardModifiers SdlEngine::GetKeyboardModifiers(const uint8_t* keyboardState, + const int scancodeCount) + { + int result = KeyboardModifiers_None; + + if (keyboardState != NULL) + { + if (SDL_SCANCODE_LSHIFT < scancodeCount && + keyboardState[SDL_SCANCODE_LSHIFT]) + { + result |= KeyboardModifiers_Shift; + } + + if (SDL_SCANCODE_RSHIFT < scancodeCount && + keyboardState[SDL_SCANCODE_RSHIFT]) + { + result |= KeyboardModifiers_Shift; + } + + if (SDL_SCANCODE_LCTRL < scancodeCount && + keyboardState[SDL_SCANCODE_LCTRL]) + { + result |= KeyboardModifiers_Control; + } + + if (SDL_SCANCODE_RCTRL < scancodeCount && + keyboardState[SDL_SCANCODE_RCTRL]) + { + result |= KeyboardModifiers_Control; + } + + if (SDL_SCANCODE_LALT < scancodeCount && + keyboardState[SDL_SCANCODE_LALT]) + { + result |= KeyboardModifiers_Alt; + } + + if (SDL_SCANCODE_RALT < scancodeCount && + keyboardState[SDL_SCANCODE_RALT]) + { + result |= KeyboardModifiers_Alt; + } + } + + return static_cast<KeyboardModifiers>(result); + } + + + void SdlEngine::SetSize(unsigned int width, + unsigned int height) + { + buffering_.SetSize(width, height, viewport_); + viewportChanged_ = true; + Refresh(); + } + + + void SdlEngine::Stop() + { + if (continue_) + { + continue_ = false; + renderFrame_.Signal(); // Unlock the render thread + renderThread_.join(); + } + } + + + void SdlEngine::Refresh() + { + renderFrame_.Signal(); + } + + + SdlEngine::SdlEngine(SdlWindow& window, + IViewport& viewport) : + window_(window), + viewport_(viewport), + continue_(true) + { + refreshEvent_ = SDL_RegisterEvents(1); + + SetSize(window_.GetWidth(), window_.GetHeight()); + + viewport_.Register(*this); + + renderThread_ = boost::thread(RenderThread, this); + } + + + SdlEngine::~SdlEngine() + { + Stop(); + + viewport_.Unregister(*this); + } + + + void SdlEngine::Run() + { + int scancodeCount = 0; + const uint8_t* keyboardState = SDL_GetKeyboardState(&scancodeCount); + + bool stop = false; + while (!stop) + { + Refresh(); + + SDL_Event event; + + while (SDL_PollEvent(&event)) + { + if (event.type == SDL_QUIT) + { + stop = true; + break; + } + else if (event.type == refreshEvent_) + { + buffering_.SwapToScreen(window_); + } + else if (event.type == SDL_MOUSEBUTTONDOWN) + { + KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); + + switch (event.button.button) + { + case SDL_BUTTON_LEFT: + viewport_.MouseDown(MouseButton_Left, event.button.x, event.button.y, modifiers); + break; + + case SDL_BUTTON_RIGHT: + viewport_.MouseDown(MouseButton_Right, event.button.x, event.button.y, modifiers); + break; + + case SDL_BUTTON_MIDDLE: + viewport_.MouseDown(MouseButton_Middle, event.button.x, event.button.y, modifiers); + break; + + default: + break; + } + } + else if (event.type == SDL_MOUSEMOTION) + { + viewport_.MouseMove(event.button.x, event.button.y); + } + else if (event.type == SDL_MOUSEBUTTONUP) + { + viewport_.MouseUp(); + } + else if (event.type == SDL_WINDOWEVENT) + { + switch (event.window.event) + { + case SDL_WINDOWEVENT_LEAVE: + viewport_.MouseLeave(); + break; + + case SDL_WINDOWEVENT_ENTER: + viewport_.MouseEnter(); + break; + + case SDL_WINDOWEVENT_SIZE_CHANGED: + SetSize(event.window.data1, event.window.data2); + break; + + default: + break; + } + } + else if (event.type == SDL_MOUSEWHEEL) + { + KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); + + int x, y; + SDL_GetMouseState(&x, &y); + + if (event.wheel.y > 0) + { + viewport_.MouseWheel(MouseWheelDirection_Up, x, y, modifiers); + } + else if (event.wheel.y < 0) + { + viewport_.MouseWheel(MouseWheelDirection_Down, x, y, modifiers); + } + } + else if (event.type == SDL_KEYDOWN) + { + KeyboardModifiers modifiers = GetKeyboardModifiers(keyboardState, scancodeCount); + + switch (event.key.keysym.sym) + { + case SDLK_a: viewport_.KeyPressed('a', modifiers); break; + case SDLK_b: viewport_.KeyPressed('b', modifiers); break; + case SDLK_c: viewport_.KeyPressed('c', modifiers); break; + case SDLK_d: viewport_.KeyPressed('d', modifiers); break; + case SDLK_e: viewport_.KeyPressed('e', modifiers); break; + case SDLK_f: window_.ToggleMaximize(); break; + case SDLK_g: viewport_.KeyPressed('g', modifiers); break; + case SDLK_h: viewport_.KeyPressed('h', modifiers); break; + case SDLK_i: viewport_.KeyPressed('i', modifiers); break; + case SDLK_j: viewport_.KeyPressed('j', modifiers); break; + case SDLK_k: viewport_.KeyPressed('k', modifiers); break; + case SDLK_l: viewport_.KeyPressed('l', modifiers); break; + case SDLK_m: viewport_.KeyPressed('m', modifiers); break; + case SDLK_n: viewport_.KeyPressed('n', modifiers); break; + case SDLK_o: viewport_.KeyPressed('o', modifiers); break; + case SDLK_p: viewport_.KeyPressed('p', modifiers); break; + case SDLK_q: stop = true; break; + case SDLK_r: viewport_.KeyPressed('r', modifiers); break; + case SDLK_s: viewport_.KeyPressed('s', modifiers); break; + case SDLK_t: viewport_.KeyPressed('t', modifiers); break; + case SDLK_u: viewport_.KeyPressed('u', modifiers); break; + case SDLK_v: viewport_.KeyPressed('v', modifiers); break; + case SDLK_w: viewport_.KeyPressed('w', modifiers); break; + case SDLK_x: viewport_.KeyPressed('x', modifiers); break; + case SDLK_y: viewport_.KeyPressed('y', modifiers); break; + case SDLK_z: viewport_.KeyPressed('z', modifiers); break; + + default: + break; + } + } + } + + SDL_Delay(10); // Necessary for mouse wheel events to work + } + + Stop(); + } + + + void SdlEngine::GlobalInitialize() + { + SDL_Init(SDL_INIT_VIDEO); + } + + + void SdlEngine::GlobalFinalize() + { + SDL_Quit(); + } +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Applications/Sdl/SdlEngine.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,89 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if ORTHANC_ENABLE_SDL == 1 + +#include "SdlBuffering.h" +#include "../../Toolbox/BinarySemaphore.h" + +#include <boost/thread.hpp> + +namespace OrthancStone +{ + class SdlEngine : public IViewport::IChangeObserver + { + private: + SdlWindow& window_; + IViewport& viewport_; + SdlBuffering buffering_; + boost::thread renderThread_; + bool continue_; + BinarySemaphore renderFrame_; + uint32_t refreshEvent_; + bool viewportChanged_; + + void RenderFrame(); + + static void RenderThread(SdlEngine* that); + + static KeyboardModifiers GetKeyboardModifiers(const uint8_t* keyboardState, + const int scancodeCount); + + void SetSize(unsigned int width, + unsigned int height); + + void Stop(); + + void Refresh(); + + public: + SdlEngine(SdlWindow& window, + IViewport& viewport); + + virtual ~SdlEngine(); + + virtual void NotifyChange(const IViewport& viewport) + { + viewportChanged_ = true; + } + + void Run(); + + static void GlobalInitialize(); + + static void GlobalFinalize(); + }; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Applications/Sdl/SdlWindow.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,161 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "SdlWindow.h" + +#if ORTHANC_ENABLE_SDL == 1 + +#include "../../Orthanc/Core/Logging.h" +#include "../../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + SdlWindow::SdlWindow(const char* title, + unsigned int width, + unsigned int height, + bool enableOpenGl) : + maximized_(false) + { + // TODO Understand why, with SDL_WINDOW_OPENGL + MinGW32 + Release + // build mode, the application crashes whenever the SDL window is + // resized or maximized + + uint32_t windowFlags, rendererFlags; + if (enableOpenGl) + { + windowFlags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE; + rendererFlags = SDL_RENDERER_ACCELERATED; + } + else + { + windowFlags = SDL_WINDOW_RESIZABLE; + rendererFlags = SDL_RENDERER_SOFTWARE; + } + + window_ = SDL_CreateWindow(title, + SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, + width, height, windowFlags); + + if (window_ == NULL) + { + LOG(ERROR) << "Cannot create the SDL window: " << SDL_GetError(); + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + renderer_ = SDL_CreateRenderer(window_, -1, rendererFlags); + if (!renderer_) + { + LOG(ERROR) << "Cannot create the SDL renderer: " << SDL_GetError(); + SDL_DestroyWindow(window_); + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + SdlWindow::~SdlWindow() + { + if (renderer_ != NULL) + { + SDL_DestroyRenderer(renderer_); + } + + if (window_ != NULL) + { + SDL_DestroyWindow(window_); + } + } + + + unsigned int SdlWindow::GetWidth() const + { + int w = -1; + SDL_GetWindowSize(window_, &w, NULL); + + if (w < 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + else + { + return static_cast<unsigned int>(w); + } + } + + + unsigned int SdlWindow::GetHeight() const + { + int h = -1; + SDL_GetWindowSize(window_, NULL, &h); + + if (h < 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + else + { + return static_cast<unsigned int>(h); + } + } + + + void SdlWindow::Render(SDL_Surface* surface) + { + //SDL_RenderClear(renderer_); + + SDL_Texture *texture = SDL_CreateTextureFromSurface(renderer_, surface); + if (texture != NULL) + { + SDL_RenderCopy(renderer_, texture, NULL, NULL); + SDL_DestroyTexture(texture); + } + + SDL_RenderPresent(renderer_); + } + + + void SdlWindow::ToggleMaximize() + { + if (maximized_) + { + SDL_RestoreWindow(window_); + maximized_ = false; + } + else + { + SDL_MaximizeWindow(window_); + maximized_ = true; + } + } +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Applications/Sdl/SdlWindow.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,68 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#if ORTHANC_ENABLE_SDL == 1 + +#include <SDL_render.h> +#include <SDL_video.h> +#include <boost/noncopyable.hpp> + +namespace OrthancStone +{ + class SdlWindow : public boost::noncopyable + { + private: + SDL_Window *window_; + SDL_Renderer *renderer_; + bool maximized_; + + public: + SdlWindow(const char* title, + unsigned int width, + unsigned int height, + bool enableOpenGl); + + ~SdlWindow(); + + unsigned int GetWidth() const; + + unsigned int GetHeight() const; + + void Render(SDL_Surface* surface); + + void ToggleMaximize(); + }; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Enumerations.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,85 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +namespace OrthancStone +{ + enum SliceOffsetMode + { + SliceOffsetMode_Absolute, + SliceOffsetMode_Relative, + SliceOffsetMode_Loop + }; + + enum ImageWindowing + { + ImageWindowing_Default, + ImageWindowing_Bone, + ImageWindowing_Lung, + ImageWindowing_Custom + }; + + enum MouseButton + { + MouseButton_Left, + MouseButton_Right, + MouseButton_Middle + }; + + enum MouseWheelDirection + { + MouseWheelDirection_Up, + MouseWheelDirection_Down + }; + + enum VolumeProjection + { + VolumeProjection_Axial, + VolumeProjection_Coronal, + VolumeProjection_Sagittal + }; + + enum ImageInterpolation + { + ImageInterpolation_Nearest, + ImageInterpolation_Linear + }; + + enum KeyboardModifiers + { + KeyboardModifiers_None = 0, + KeyboardModifiers_Shift = (1 << 0), + KeyboardModifiers_Control = (1 << 1), + KeyboardModifiers_Alt = (1 << 2) + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/CircleMeasureTracker.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,122 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#define _USE_MATH_DEFINES // To access M_PI in Visual Studio +#include <cmath> + +#include "CircleMeasureTracker.h" + +#include "../Viewport/CairoFont.h" + +#include <stdio.h> + +namespace OrthancStone +{ + CircleMeasureTracker::CircleMeasureTracker(IStatusBar* statusBar, + const SliceGeometry& slice, + double x, + double y, + uint8_t red, + uint8_t green, + uint8_t blue, + unsigned int fontSize) : + statusBar_(statusBar), + slice_(slice), + x1_(x), + y1_(y), + x2_(x), + y2_(y), + fontSize_(fontSize) + { + color_[0] = red; + color_[1] = green; + color_[2] = blue; + } + + + void CircleMeasureTracker::Render(CairoContext& context, + double zoom) + { + double x = (x1_ + x2_) / 2.0; + double y = (y1_ + y2_) / 2.0; + + Vector tmp; + GeometryToolbox::AssignVector(tmp, x2_ - x1_, y2_ - y1_); + double r = boost::numeric::ublas::norm_2(tmp) / 2.0; + + context.SetSourceColor(color_[0], color_[1], color_[2]); + + cairo_t* cr = context.GetObject(); + cairo_save(cr); + cairo_set_line_width(cr, 2.0 / zoom); + cairo_translate(cr, x, y); + cairo_arc(cr, 0, 0, r, 0, 2 * M_PI); + cairo_stroke_preserve(cr); + cairo_stroke(cr); + cairo_restore(cr); + + if (fontSize_ != 0) + { + cairo_move_to(cr, x, y); + CairoFont font("sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + font.Draw(context, FormatRadius(), static_cast<double>(fontSize_) / zoom); + } + } + + + double CircleMeasureTracker::GetRadius() const // In millimeters + { + Vector a = slice_.MapSliceToWorldCoordinates(x1_, y1_); + Vector b = slice_.MapSliceToWorldCoordinates(x2_, y2_); + return boost::numeric::ublas::norm_2(b - a) / 2.0; + } + + + std::string CircleMeasureTracker::FormatRadius() const + { + char buf[64]; + sprintf(buf, "%0.01f cm", GetRadius() / 10.0); + return buf; + } + + void CircleMeasureTracker::MouseMove(double x, + double y) + { + x2_ = x; + y2_ = y; + + if (statusBar_ != NULL) + { + statusBar_->SetMessage("Circle radius: " + FormatRadius()); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/CircleMeasureTracker.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,79 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Widgets/IWorldSceneMouseTracker.h" + +#include "../Viewport/IStatusBar.h" +#include "../Toolbox/SliceGeometry.h" + +namespace OrthancStone +{ + class CircleMeasureTracker : public IWorldSceneMouseTracker + { + private: + IStatusBar* statusBar_; + SliceGeometry slice_; + double x1_; + double y1_; + double x2_; + double y2_; + uint8_t color_[3]; + unsigned int fontSize_; + + public: + CircleMeasureTracker(IStatusBar* statusBar, + const SliceGeometry& slice, + double x, + double y, + uint8_t red, + uint8_t green, + uint8_t blue, + unsigned int fontSize); + + virtual void Render(CairoContext& context, + double zoom); + + double GetRadius() const; // In millimeters + + std::string FormatRadius() const; + + virtual void MouseUp() + { + // Possibly create a new landmark "volume" with the circle in subclasses + } + + virtual void MouseMove(double x, + double y); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/ColorFrameRenderer.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,70 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "ColorFrameRenderer.h" + +#include "../Orthanc/Core/OrthancException.h" +#include "../Orthanc/Core/Images/ImageProcessing.h" + +namespace OrthancStone +{ + CairoSurface* ColorFrameRenderer::GenerateDisplay(const RenderStyle& style) + { + std::auto_ptr<CairoSurface> display(new CairoSurface(frame_->GetWidth(), frame_->GetHeight())); + + Orthanc::ImageAccessor target = display->GetAccessor(); + Orthanc::ImageProcessing::Convert(target, *frame_); + + return display.release(); + } + + + ColorFrameRenderer::ColorFrameRenderer(Orthanc::ImageAccessor* frame, + const SliceGeometry& viewportSlice, + const SliceGeometry& frameSlice, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality) : + FrameRenderer(viewportSlice, frameSlice, pixelSpacingX, pixelSpacingY, isFullQuality), + frame_(frame) + { + if (frame == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + if (frame_->GetFormat() != Orthanc::PixelFormat_RGB24) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/ColorFrameRenderer.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,55 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "FrameRenderer.h" + +namespace OrthancStone +{ + class ColorFrameRenderer : public FrameRenderer + { + private: + std::auto_ptr<Orthanc::ImageAccessor> frame_; // In RGB24 + + protected: + virtual CairoSurface* GenerateDisplay(const RenderStyle& style); + + public: + ColorFrameRenderer(Orthanc::ImageAccessor* frame, // Takes ownership + const SliceGeometry& viewportSlice, + const SliceGeometry& frameSlice, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/DicomStructureSetRendererFactory.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,97 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "DicomStructureSetRendererFactory.h" + +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + class DicomStructureSetRendererFactory::Renderer : public ILayerRenderer + { + private: + const DicomStructureSet& structureSet_; + SliceGeometry slice_; + bool visible_; + + public: + Renderer(const DicomStructureSet& structureSet, + const SliceGeometry& slice) : + structureSet_(structureSet), + slice_(slice), + visible_(true) + { + } + + virtual bool RenderLayer(CairoContext& context, + const ViewportGeometry& view) + { + if (visible_) + { + cairo_set_line_width(context.GetObject(), 3.0f / view.GetZoom()); + structureSet_.Render(context, slice_); + } + + return true; + } + + virtual void SetLayerStyle(const RenderStyle& style) + { + visible_ = style.visible_; + } + + virtual bool IsFullQuality() + { + return true; + } + }; + + + ILayerRenderer* DicomStructureSetRendererFactory::CreateLayerRenderer(const SliceGeometry& displaySlice) + { + bool isOpposite; + if (GeometryToolbox::IsParallelOrOpposite(isOpposite, displaySlice.GetNormal(), structureSet_.GetNormal())) + { + return new Renderer(structureSet_, displaySlice); + } + else + { + return NULL; + } + } + + + ISliceableVolume& DicomStructureSetRendererFactory::GetSourceVolume() const + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/DicomStructureSetRendererFactory.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,71 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Toolbox/DicomStructureSet.h" +#include "ILayerRendererFactory.h" + +namespace OrthancStone +{ + class DicomStructureSetRendererFactory : public ILayerRendererFactory + { + private: + class Renderer; + + const DicomStructureSet& structureSet_; + + public: + DicomStructureSetRendererFactory(const DicomStructureSet& structureSet) : + structureSet_(structureSet) + { + } + + virtual bool GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& displaySlice) + { + return false; + } + + virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& displaySlice); + + virtual bool HasSourceVolume() const + { + return false; + } + + virtual ISliceableVolume& GetSourceVolume() const; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/FrameRenderer.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,249 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "FrameRenderer.h" + +#include "GrayscaleFrameRenderer.h" +#include "ColorFrameRenderer.h" + +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + static bool ComputePixelTransform(cairo_matrix_t& target, + const SliceGeometry& viewportSlice, + const SliceGeometry& frameSlice, + double pixelSpacingX, + double pixelSpacingY) + { + bool isOpposite; + if (!GeometryToolbox::IsParallelOrOpposite(isOpposite, viewportSlice.GetNormal(), frameSlice.GetNormal())) + { + return false; + } + else + { + double x0, y0, x1, y1, x2, y2; + viewportSlice.ProjectPoint(x0, y0, frameSlice.GetOrigin() + - 0.5 * pixelSpacingX * frameSlice.GetAxisX() + - 0.5 * pixelSpacingY * frameSlice.GetAxisY()); + viewportSlice.ProjectPoint(x1, y1, frameSlice.GetOrigin() + + 0.5 * pixelSpacingX * frameSlice.GetAxisX() + - 0.5 * pixelSpacingY * frameSlice.GetAxisY()); + viewportSlice.ProjectPoint(x2, y2, frameSlice.GetOrigin() + - 0.5 * pixelSpacingX * frameSlice.GetAxisX() + + 0.5 * pixelSpacingY * frameSlice.GetAxisY()); + + /** + * Now we solve the system of linear equations Ax + b = x', given: + * A [0 ; 0] + b = [x0 ; y0] + * A [1 ; 0] + b = [x1 ; y1] + * A [0 ; 1] + b = [x2 ; y2] + * <=> + * b = [x0 ; y0] + * A [1 ; 0] = [x1 ; y1] - b = [x1 - x0 ; y1 - y0] + * A [0 ; 1] = [x2 ; y2] - b = [x2 - x0 ; y2 - y0] + * <=> + * b = [x0 ; y0] + * [a11 ; a21] = [x1 - x0 ; y1 - y0] + * [a12 ; a22] = [x2 - x0 ; y2 - y0] + **/ + + cairo_matrix_init(&target, x1 - x0, y1 - y0, x2 - x0, y2 - y0, x0, y0); + + return true; + } + } + + + FrameRenderer::FrameRenderer(const SliceGeometry& viewportSlice, + const SliceGeometry& frameSlice, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality) : + viewportSlice_(viewportSlice), + frameSlice_(frameSlice), + pixelSpacingX_(pixelSpacingX), + pixelSpacingY_(pixelSpacingY), + isFullQuality_(isFullQuality) + { + } + + + bool FrameRenderer::ComputeFrameExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice, + const SliceGeometry& frameSlice, + unsigned int frameWidth, + unsigned int frameHeight, + double pixelSpacingX, + double pixelSpacingY) + { + bool isOpposite; + if (!GeometryToolbox::IsParallelOrOpposite(isOpposite, viewportSlice.GetNormal(), frameSlice.GetNormal())) + { + return false; + } + else + { + cairo_matrix_t transform; + if (!ComputePixelTransform(transform, viewportSlice, frameSlice, pixelSpacingX, pixelSpacingY)) + { + return true; + } + + x1 = 0; + y1 = 0; + cairo_matrix_transform_point(&transform, &x1, &y1); + + x2 = frameWidth; + y2 = frameHeight; + cairo_matrix_transform_point(&transform, &x2, &y2); + + if (x1 > x2) + { + std::swap(x1, x2); + } + + if (y1 > y2) + { + std::swap(y1, y2); + } + + return true; + } + } + + + bool FrameRenderer::RenderLayer(CairoContext& context, + const ViewportGeometry& view) + { + if (!style_.visible_) + { + return true; + } + + if (display_.get() == NULL) + { + if (!ComputePixelTransform(transform_, viewportSlice_, frameSlice_, pixelSpacingX_, pixelSpacingY_)) + { + return true; + } + + display_.reset(GenerateDisplay(style_)); + } + + assert(display_.get() != NULL); + + cairo_t *cr = context.GetObject(); + + cairo_save(cr); + + cairo_transform(cr, &transform_); + //cairo_set_operator(cr, CAIRO_OPERATOR_OVER); + cairo_set_source_surface(cr, display_->GetObject(), 0, 0); + + switch (style_.interpolation_) + { + case ImageInterpolation_Nearest: + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_NEAREST); + break; + + case ImageInterpolation_Linear: + cairo_pattern_set_filter(cairo_get_source(cr), CAIRO_FILTER_BILINEAR); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + cairo_paint_with_alpha(cr, style_.alpha_); + + if (style_.drawGrid_) + { + context.SetSourceColor(style_.drawColor_); + cairo_set_line_width(cr, 0.5 / view.GetZoom()); + + for (unsigned int x = 0; x <= display_->GetWidth(); x++) + { + cairo_move_to(cr, x, 0); + cairo_line_to(cr, x, display_->GetHeight()); + } + + for (unsigned int y = 0; y <= display_->GetHeight(); y++) + { + cairo_move_to(cr, 0, y); + cairo_line_to(cr, display_->GetWidth(), y); + } + + cairo_stroke(cr); + } + + cairo_restore(cr); + + return true; + } + + + void FrameRenderer::SetLayerStyle(const RenderStyle& style) + { + style_ = style; + display_.reset(NULL); + } + + + ILayerRenderer* FrameRenderer::CreateRenderer(Orthanc::ImageAccessor* frame, + const SliceGeometry& viewportSlice, + const SliceGeometry& frameSlice, + const DicomDataset& dicom, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality) + { + std::auto_ptr<Orthanc::ImageAccessor> protect(frame); + + if (frame->GetFormat() == Orthanc::PixelFormat_RGB24) + { + return new ColorFrameRenderer(protect.release(), viewportSlice, frameSlice, + pixelSpacingX, pixelSpacingY, isFullQuality); + } + else + { + DicomFrameConverter converter; + converter.ReadParameters(dicom); + return new GrayscaleFrameRenderer(protect.release(), converter, viewportSlice, frameSlice, + pixelSpacingX, pixelSpacingY, isFullQuality); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/FrameRenderer.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,93 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ILayerRenderer.h" + +#include "../Toolbox/SliceGeometry.h" + +namespace OrthancStone +{ + class FrameRenderer : public ILayerRenderer + { + private: + SliceGeometry viewportSlice_; + SliceGeometry frameSlice_; + double pixelSpacingX_; + double pixelSpacingY_; + RenderStyle style_; + bool isFullQuality_; + + std::auto_ptr<CairoSurface> display_; + cairo_matrix_t transform_; + + protected: + virtual CairoSurface* GenerateDisplay(const RenderStyle& style) = 0; + + public: + FrameRenderer(const SliceGeometry& viewportSlice, + const SliceGeometry& frameSlice, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality); + + static bool ComputeFrameExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice, + const SliceGeometry& frameSlice, + unsigned int frameWidth, + unsigned int frameHeight, + double pixelSpacingX, + double pixelSpacingY); + + virtual bool RenderLayer(CairoContext& context, + const ViewportGeometry& view); + + virtual void SetLayerStyle(const RenderStyle& style); + + virtual bool IsFullQuality() + { + return isFullQuality_; + } + + static ILayerRenderer* CreateRenderer(Orthanc::ImageAccessor* frame, + const SliceGeometry& viewportSlice, + const SliceGeometry& frameSlice, + const DicomDataset& dicom, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/GrayscaleFrameRenderer.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,144 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "GrayscaleFrameRenderer.h" + +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + CairoSurface* GrayscaleFrameRenderer::GenerateDisplay(const RenderStyle& style) + { + std::auto_ptr<CairoSurface> result; + + float windowCenter, windowWidth; + style.ComputeWindowing(windowCenter, windowWidth, + defaultWindowCenter_, defaultWindowWidth_); + + float x0 = windowCenter - windowWidth / 2.0f; + float x1 = windowCenter + windowWidth / 2.0f; + + //LOG(INFO) << "Window: " << x0 << " => " << x1; + + result.reset(new CairoSurface(frame_->GetWidth(), frame_->GetHeight())); + + const uint8_t* lut = NULL; + if (style.applyLut_) + { + if (Orthanc::EmbeddedResources::GetFileResourceSize(style.lut_) != 3 * 256) + { + // Invalid colormap + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + lut = reinterpret_cast<const uint8_t*>(Orthanc::EmbeddedResources::GetFileResourceBuffer(style.lut_)); + } + + Orthanc::ImageAccessor target = result->GetAccessor(); + for (unsigned int y = 0; y < target.GetHeight(); y++) + { + const float* p = reinterpret_cast<const float*>(frame_->GetConstRow(y)); + uint8_t* q = reinterpret_cast<uint8_t*>(target.GetRow(y)); + + for (unsigned int x = 0; x < target.GetWidth(); x++, p++, q += 4) + { + uint8_t v = 0; + if (windowWidth >= 0.001f) // Avoid division by zero + { + if (*p >= x1) + { + v = 255; + } + else if (*p <= x0) + { + v = 0; + } + else + { + // https://en.wikipedia.org/wiki/Linear_interpolation + v = static_cast<uint8_t>(255.0f * (*p - x0) / (x1 - x0)); + } + + if (style.reverse_) + { + v = 255 - v; + } + } + + if (style.applyLut_) + { + assert(lut != NULL); + q[3] = 255; + q[2] = lut[3 * v]; + q[1] = lut[3 * v + 1]; + q[0] = lut[3 * v + 2]; + } + else + { + q[3] = 255; + q[2] = v; + q[1] = v; + q[0] = v; + } + } + } + + return result.release(); + } + + + GrayscaleFrameRenderer::GrayscaleFrameRenderer(Orthanc::ImageAccessor* frame, + const DicomFrameConverter& converter, + const SliceGeometry& viewportSlice, + const SliceGeometry& frameSlice, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality) : + FrameRenderer(viewportSlice, frameSlice, pixelSpacingX, pixelSpacingY, isFullQuality), + frame_(frame), + defaultWindowCenter_(converter.GetDefaultWindowCenter()), + defaultWindowWidth_(converter.GetDefaultWindowWidth()) + { + if (frame == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + converter.ConvertFrame(frame_); + assert(frame_.get() != NULL); + + if (frame_->GetFormat() != Orthanc::PixelFormat_Float32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/GrayscaleFrameRenderer.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,59 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "FrameRenderer.h" +#include "../Toolbox/DicomFrameConverter.h" + +namespace OrthancStone +{ + class GrayscaleFrameRenderer : public FrameRenderer + { + private: + std::auto_ptr<Orthanc::ImageAccessor> frame_; // In Float32 + float defaultWindowCenter_; + float defaultWindowWidth_; + + protected: + virtual CairoSurface* GenerateDisplay(const RenderStyle& style); + + public: + GrayscaleFrameRenderer(Orthanc::ImageAccessor* frame, // Takes ownership + const DicomFrameConverter& converter, + const SliceGeometry& viewportSlice, + const SliceGeometry& frameSlice, + double pixelSpacingX, + double pixelSpacingY, + bool isFullQuality); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/ILayerRenderer.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,52 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Viewport/CairoContext.h" +#include "../Toolbox/IThreadSafety.h" +#include "../Toolbox/ViewportGeometry.h" +#include "RenderStyle.h" + +namespace OrthancStone +{ + class ILayerRenderer : public IThreadUnsafe + { + public: + virtual bool RenderLayer(CairoContext& context, + const ViewportGeometry& view) = 0; + + virtual void SetLayerStyle(const RenderStyle& style) = 0; + + virtual bool IsFullQuality() = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/ILayerRendererFactory.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,63 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ILayerRenderer.h" +#include "../Toolbox/SliceGeometry.h" +#include "../Volumes/ISliceableVolume.h" + +namespace OrthancStone +{ + class ILayerRendererFactory : public IThreadUnsafe + { + public: + virtual ~ILayerRendererFactory() + { + } + + virtual bool GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& displaySlice) = 0; + + // This operation can be slow, as it might imply the download of a + // slice from Orthanc. The result might be NULL, if the slice is + // not compatible with the underlying source volume. + virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& displaySlice) = 0; + + virtual bool HasSourceVolume() const = 0; + + virtual ISliceableVolume& GetSourceVolume() const = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/LineLayerRenderer.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,76 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "LineLayerRenderer.h" + +namespace OrthancStone +{ + LineLayerRenderer::LineLayerRenderer(double x1, + double y1, + double x2, + double y2) : + x1_(x1), + y1_(y1), + x2_(x2), + y2_(y2) + { + RenderStyle style; + SetLayerStyle(style); + } + + + bool LineLayerRenderer::RenderLayer(CairoContext& context, + const ViewportGeometry& view) + { + if (visible_) + { + context.SetSourceColor(color_); + + cairo_t *cr = context.GetObject(); + cairo_set_line_width(cr, 1.0 / view.GetZoom()); + cairo_move_to(cr, x1_, y1_); + cairo_line_to(cr, x2_, y2_); + cairo_stroke(cr); + } + + return true; + } + + + void LineLayerRenderer::SetLayerStyle(const RenderStyle& style) + { + visible_ = style.visible_; + color_[0] = style.drawColor_[0]; + color_[1] = style.drawColor_[1]; + color_[2] = style.drawColor_[2]; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/LineLayerRenderer.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,65 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ILayerRenderer.h" + +namespace OrthancStone +{ + class LineLayerRenderer : public ILayerRenderer + { + private: + double x1_; + double y1_; + double x2_; + double y2_; + bool visible_; + uint8_t color_[3]; + + public: + LineLayerRenderer(double x1, + double y1, + double x2, + double y2); + + virtual bool RenderLayer(CairoContext& context, + const ViewportGeometry& view); + + virtual void SetLayerStyle(const RenderStyle& style); + + virtual bool IsFullQuality() + { + return true; + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/LineMeasureTracker.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,109 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "LineMeasureTracker.h" + +#include "../Viewport/CairoFont.h" + +#include <stdio.h> + +namespace OrthancStone +{ + LineMeasureTracker::LineMeasureTracker(IStatusBar* statusBar, + const SliceGeometry& slice, + double x, + double y, + uint8_t red, + uint8_t green, + uint8_t blue, + unsigned int fontSize) : + statusBar_(statusBar), + slice_(slice), + x1_(x), + y1_(y), + x2_(x), + y2_(y), + fontSize_(fontSize) + { + color_[0] = red; + color_[1] = green; + color_[2] = blue; + } + + + void LineMeasureTracker::Render(CairoContext& context, + double zoom) + { + context.SetSourceColor(color_[0], color_[1], color_[2]); + + cairo_t* cr = context.GetObject(); + cairo_set_line_width(cr, 2.0 / zoom); + cairo_move_to(cr, x1_, y1_); + cairo_line_to(cr, x2_, y2_); + cairo_stroke(cr); + + if (fontSize_ != 0) + { + cairo_move_to(cr, x2_, y2_ - static_cast<double>(fontSize_) / zoom); + CairoFont font("sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL); + font.Draw(context, FormatLength(), static_cast<double>(fontSize_) / zoom); + } + } + + + double LineMeasureTracker::GetLength() const // In millimeters + { + Vector a = slice_.MapSliceToWorldCoordinates(x1_, y1_); + Vector b = slice_.MapSliceToWorldCoordinates(x2_, y2_); + return boost::numeric::ublas::norm_2(b - a); + } + + + std::string LineMeasureTracker::FormatLength() const + { + char buf[64]; + sprintf(buf, "%0.01f cm", GetLength() / 10.0); + return buf; + } + + void LineMeasureTracker::MouseMove(double x, + double y) + { + x2_ = x; + y2_ = y; + + if (statusBar_ != NULL) + { + statusBar_->SetMessage("Line length: " + FormatLength()); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/LineMeasureTracker.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,79 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Widgets/IWorldSceneMouseTracker.h" + +#include "../Viewport/IStatusBar.h" +#include "../Toolbox/SliceGeometry.h" + +namespace OrthancStone +{ + class LineMeasureTracker : public IWorldSceneMouseTracker + { + private: + IStatusBar* statusBar_; + SliceGeometry slice_; + double x1_; + double y1_; + double x2_; + double y2_; + uint8_t color_[3]; + unsigned int fontSize_; + + public: + LineMeasureTracker(IStatusBar* statusBar, + const SliceGeometry& slice, + double x, + double y, + uint8_t red, + uint8_t green, + uint8_t blue, + unsigned int fontSize); + + virtual void Render(CairoContext& context, + double zoom); + + double GetLength() const; // In millimeters + + std::string FormatLength() const; + + virtual void MouseUp() + { + // Possibly create a new landmark "volume" with the line in subclasses + } + + virtual void MouseMove(double x, + double y); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/RenderStyle.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,99 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "RenderStyle.h" + +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + RenderStyle::RenderStyle() + { + visible_ = true; + reverse_ = false; + windowing_ = ImageWindowing_Default; + alpha_ = 1; + applyLut_ = false; + lut_ = Orthanc::EmbeddedResources::COLORMAP_HOT; + drawGrid_ = false; + drawColor_[0] = 255; + drawColor_[1] = 255; + drawColor_[2] = 255; + customWindowCenter_ = 128; + customWindowWidth_ = 256; + interpolation_ = ImageInterpolation_Nearest; + fontSize_ = 14; + } + + + void RenderStyle::ComputeWindowing(float& targetCenter, + float& targetWidth, + float defaultCenter, + float defaultWidth) const + { + switch (windowing_) + { + case ImageWindowing_Default: + targetCenter = defaultCenter; + targetWidth = defaultWidth; + break; + + case ImageWindowing_Bone: + targetCenter = 300; + targetWidth = 2000; + break; + + case ImageWindowing_Lung: + targetCenter = -600; + targetWidth = 1600; + break; + + case ImageWindowing_Custom: + targetCenter = customWindowCenter_; + targetWidth = customWindowWidth_; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + void RenderStyle::SetColor(uint8_t red, + uint8_t green, + uint8_t blue) + { + drawColor_[0] = red; + drawColor_[1] = green; + drawColor_[2] = blue; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/RenderStyle.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,69 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Enumerations.h" + +#include <EmbeddedResources.h> + +#include <stdint.h> + +namespace OrthancStone +{ + struct RenderStyle + { + bool visible_; + bool reverse_; + ImageWindowing windowing_; + float alpha_; // In [0,1] + bool applyLut_; + Orthanc::EmbeddedResources::FileResourceId lut_; + bool drawGrid_; + uint8_t drawColor_[3]; + float customWindowCenter_; + float customWindowWidth_; + ImageInterpolation interpolation_; + unsigned int fontSize_; + + RenderStyle(); + + void ComputeWindowing(float& targetCenter, + float& targetWidth, + float defaultCenter, + float defaultWidth) const; + + void SetColor(uint8_t red, + uint8_t green, + uint8_t blue); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/SeriesFrameRendererFactory.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,176 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "SeriesFrameRendererFactory.h" + +#include "FrameRenderer.h" +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + void SeriesFrameRendererFactory::ReadCurrentFrameDataset(size_t frame) + { + if (currentDataset_.get() != NULL && + (fast_ || currentFrame_ == frame)) + { + // The frame has not changed since the previous call, no need to + // update the DICOM dataset + return; + } + + currentDataset_.reset(loader_->DownloadDicom(frame)); + currentFrame_ = frame; + + if (currentDataset_.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + void SeriesFrameRendererFactory::GetCurrentPixelSpacing(double& spacingX, + double& spacingY) const + { + if (currentDataset_.get() == NULL) + { + // There was no previous call "ReadCurrentFrameDataset()" + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + currentDataset_->GetPixelSpacing(spacingX, spacingY); + } + + + double SeriesFrameRendererFactory::GetCurrentSliceThickness() const + { + if (currentDataset_.get() == NULL) + { + // There was no previous call "ReadCurrentFrameDataset()" + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + if (currentDataset_->HasTag(DICOM_TAG_SLICE_THICKNESS)) + { + return currentDataset_->GetFloatValue(DICOM_TAG_SLICE_THICKNESS); + } + else + { + // Some arbitrary large slice thickness + return std::numeric_limits<double>::infinity(); + } + } + + + SeriesFrameRendererFactory::SeriesFrameRendererFactory(ISeriesLoader* loader, // Takes ownership + bool fast) : + loader_(loader), + currentFrame_(0), + fast_(fast) + { + if (loader == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + bool SeriesFrameRendererFactory::GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice) + { + if (currentDataset_.get() == NULL) + { + // There has been no previous call to + // "CreateLayerRenderer". Read some arbitrary DICOM frame, the + // one at the middle of the series. + unsigned int depth = loader_->GetGeometry().GetSliceCount(); + ReadCurrentFrameDataset(depth / 2); + } + + double spacingX, spacingY; + GetCurrentPixelSpacing(spacingX, spacingY); + + return FrameRenderer::ComputeFrameExtent(x1, y1, x2, y2, + viewportSlice, + loader_->GetGeometry().GetSlice(0), + loader_->GetWidth(), + loader_->GetHeight(), + spacingX, spacingY); + } + + + ILayerRenderer* SeriesFrameRendererFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice) + { + size_t closest; + double distance; + + bool isOpposite; + if (!GeometryToolbox::IsParallelOrOpposite(isOpposite, loader_->GetGeometry().GetNormal(), viewportSlice.GetNormal()) || + !loader_->GetGeometry().ComputeClosestSlice(closest, distance, viewportSlice.GetOrigin())) + { + // Unable to compute the slice in the series that is the + // closest to the slice displayed by the viewport + return NULL; + } + + ReadCurrentFrameDataset(closest); + assert(currentDataset_.get() != NULL); + + double spacingX, spacingY; + GetCurrentPixelSpacing(spacingX, spacingY); + + if (distance <= GetCurrentSliceThickness() / 2.0) + { + SliceGeometry frameSlice(*currentDataset_); + return FrameRenderer::CreateRenderer(loader_->DownloadFrame(closest), + viewportSlice, + frameSlice, + *currentDataset_, + spacingX, spacingY, + true); + } + else + { + // The closest slice of the series is too far away from the + // slice displayed by the viewport + return NULL; + } + } + + + ISliceableVolume& SeriesFrameRendererFactory::GetSourceVolume() const + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/SeriesFrameRendererFactory.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,75 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ILayerRendererFactory.h" + +#include "../Toolbox/ISeriesLoader.h" + +namespace OrthancStone +{ + class SeriesFrameRendererFactory : public ILayerRendererFactory + { + private: + std::auto_ptr<ISeriesLoader> loader_; + std::auto_ptr<DicomDataset> currentDataset_; + size_t currentFrame_; + bool fast_; + + void ReadCurrentFrameDataset(size_t frame); + + void GetCurrentPixelSpacing(double& spacingX, + double& spacingY) const; + + double GetCurrentSliceThickness() const; + + public: + SeriesFrameRendererFactory(ISeriesLoader* loader, // Takes ownership + bool fast); + + virtual bool GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice); + + virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& viewportSlice); + + virtual bool HasSourceVolume() const + { + return false; + } + + virtual ISliceableVolume& GetSourceVolume() const; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/SiblingSliceLocationFactory.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,159 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "SiblingSliceLocationFactory.h" + +#include "LineLayerRenderer.h" + +namespace OrthancStone +{ + SiblingSliceLocationFactory::SiblingSliceLocationFactory(LayeredSceneWidget& owner, + LayeredSceneWidget& sibling) : + owner_(owner), + sibling_(sibling), + hasLayerIndex_(false) + { + style_.SetColor(0, 255, 0); + slice_ = sibling.GetSlice(); + sibling_.Register(*this); + } + + + void SiblingSliceLocationFactory::NotifySliceChange(const LayeredSceneWidget& source, + const SliceGeometry& slice) + { + if (&source == &sibling_) + { + SetSlice(slice); + } + } + + + void SiblingSliceLocationFactory::SetLayerIndex(size_t layerIndex) + { + boost::mutex::scoped_lock lock(mutex_); + hasLayerIndex_ = true; + layerIndex_ = layerIndex; + } + + + void SiblingSliceLocationFactory::SetStyle(const RenderStyle& style) + { + boost::mutex::scoped_lock lock(mutex_); + style_ = style; + } + + + RenderStyle SiblingSliceLocationFactory::GetRenderStyle() + { + boost::mutex::scoped_lock lock(mutex_); + return style_; + } + + + void SiblingSliceLocationFactory::SetSlice(const SliceGeometry& slice) + { + boost::mutex::scoped_lock lock(mutex_); + slice_ = slice; + + if (hasLayerIndex_) + { + owner_.InvalidateLayer(layerIndex_); + } + } + + + ILayerRenderer* SiblingSliceLocationFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice) + { + Vector p, d; + RenderStyle style; + + { + boost::mutex::scoped_lock lock(mutex_); + + style = style_; + + // Compute the line of intersection between the two slices + if (!GeometryToolbox::IntersectTwoPlanes(p, d, + slice_.GetOrigin(), slice_.GetNormal(), + viewportSlice.GetOrigin(), viewportSlice.GetNormal())) + { + // The two slice are parallel, don't try and display the intersection + return NULL; + } + } + + double x1, y1, x2, y2; + viewportSlice.ProjectPoint(x1, y1, p); + viewportSlice.ProjectPoint(x2, y2, p + 1000.0 * d); + + double sx1, sy1, sx2, sy2; + owner_.GetView().GetSceneExtent(sx1, sy1, sx2, sy2); + + if (GeometryToolbox::ClipLineToRectangle(x1, y1, x2, y2, + x1, y1, x2, y2, + sx1, sy1, sx2, sy2)) + { + std::auto_ptr<ILayerRenderer> layer(new LineLayerRenderer(x1, y1, x2, y2)); + layer->SetLayerStyle(style); + return layer.release(); + } + else + { + // Parallel slices + return NULL; + } + } + + + ISliceableVolume& SiblingSliceLocationFactory::GetSourceVolume() const + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + void SiblingSliceLocationFactory::Configure(LayeredSceneWidget& a, + LayeredSceneWidget& b) + { + { + size_t layerIndex; + ILayerRendererFactory& factory = a.AddLayer(layerIndex, new SiblingSliceLocationFactory(a, b)); + dynamic_cast<SiblingSliceLocationFactory&>(factory).SetLayerIndex(layerIndex); + } + + { + size_t layerIndex; + ILayerRendererFactory& factory = b.AddLayer(layerIndex, new SiblingSliceLocationFactory(b, a)); + dynamic_cast<SiblingSliceLocationFactory&>(factory).SetLayerIndex(layerIndex); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/SiblingSliceLocationFactory.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,89 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Widgets/LayeredSceneWidget.h" + +namespace OrthancStone +{ + class SiblingSliceLocationFactory : + public ILayerRendererFactory, + public LayeredSceneWidget::ISliceObserver + { + private: + boost::mutex mutex_; + LayeredSceneWidget& owner_; + LayeredSceneWidget& sibling_; + SliceGeometry slice_; + RenderStyle style_; + bool hasLayerIndex_; + size_t layerIndex_; + + + public: + SiblingSliceLocationFactory(LayeredSceneWidget& owner, + LayeredSceneWidget& sibling); + + virtual void NotifySliceChange(const LayeredSceneWidget& source, + const SliceGeometry& slice); + + void SetLayerIndex(size_t layerIndex); + + void SetStyle(const RenderStyle& style); + + RenderStyle GetRenderStyle(); + + void SetSlice(const SliceGeometry& slice); + + virtual bool GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice) + { + return false; + } + + virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& viewportSlice); + + virtual bool HasSourceVolume() const + { + return false; + } + + virtual ISliceableVolume& GetSourceVolume() const; + + static void Configure(LayeredSceneWidget& a, + LayeredSceneWidget& b); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/SingleFrameRendererFactory.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,102 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "SingleFrameRendererFactory.h" + +#include "FrameRenderer.h" +#include "../Messaging/MessagingToolbox.h" +#include "../Orthanc/Core/OrthancException.h" +#include "../Toolbox/DicomFrameConverter.h" + +namespace OrthancStone +{ + SingleFrameRendererFactory::SingleFrameRendererFactory(IOrthancConnection& orthanc, + const std::string& instanceId, + unsigned int frame) : + orthanc_(orthanc), + dicom_(orthanc, instanceId), + instance_(instanceId), + frame_(frame) + { + DicomFrameConverter converter; + converter.ReadParameters(dicom_); + format_ = converter.GetExpectedPixelFormat(); + } + + + SliceGeometry SingleFrameRendererFactory::GetSliceGeometry() + { + if (dicom_.HasTag(DICOM_TAG_IMAGE_POSITION_PATIENT) && + dicom_.HasTag(DICOM_TAG_IMAGE_ORIENTATION_PATIENT)) + { + return SliceGeometry(dicom_); + } + else + { + return SliceGeometry(); + } + } + + + bool SingleFrameRendererFactory::GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice) + { + // Assume that PixelSpacingX == PixelSpacingY == 1 + + unsigned int width = dicom_.GetUnsignedIntegerValue(DICOM_TAG_COLUMNS); + unsigned int height = dicom_.GetUnsignedIntegerValue(DICOM_TAG_ROWS); + + x1 = 0; + y1 = 0; + x2 = static_cast<double>(width); + y2 = static_cast<double>(height); + + return true; + } + + + ILayerRenderer* SingleFrameRendererFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice) + { + SliceGeometry frameSlice(dicom_); + return FrameRenderer::CreateRenderer(MessagingToolbox::DecodeFrame(orthanc_, instance_, frame_, format_), + viewportSlice, frameSlice, dicom_, 1, 1, true); + } + + + ISliceableVolume& SingleFrameRendererFactory::GetSourceVolume() const + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Layers/SingleFrameRendererFactory.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,75 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ILayerRendererFactory.h" + +namespace OrthancStone +{ + class SingleFrameRendererFactory : public ILayerRendererFactory + { + private: + IOrthancConnection& orthanc_; + DicomDataset dicom_; + std::string instance_; + unsigned int frame_; + Orthanc::PixelFormat format_; + + public: + SingleFrameRendererFactory(IOrthancConnection& orthanc, + const std::string& instanceId, + unsigned int frame); + + const DicomDataset& GetDataset() const + { + return dicom_; + } + + SliceGeometry GetSliceGeometry(); + + virtual bool GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice); + + virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& viewportSlice); + + virtual bool HasSourceVolume() const + { + return false; + } + + virtual ISliceableVolume& GetSourceVolume() const; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Messaging/CurlOrthancConnection.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,83 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "CurlOrthancConnection.h" + +#if ORTHANC_ENABLE_CURL == 1 + +#include "../Orthanc/Core/HttpClient.h" +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + void CurlOrthancConnection::RestApiGet(std::string& result, + const std::string& uri) + { + /** + * TODO: This function sometimes crashes if compiled with + * MinGW-W64 (32 bit) in Release mode, on Windows XP. Introducing + * a mutex here fixes the issue. Not sure of what is the + * culprit. Maybe a bug in a old version of MinGW? + **/ + + Orthanc::HttpClient client(parameters_, uri); + + // Don't follow 3xx HTTP (avoid redirections to "unsupported.png" in Orthanc) + client.SetRedirectionFollowed(false); + + if (!client.Apply(result)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); + } + } + + + void CurlOrthancConnection::RestApiPost(std::string& result, + const std::string& uri, + const std::string& body) + { + Orthanc::HttpClient client(parameters_, uri); + + // Don't follow 3xx HTTP (avoid redirections to "unsupported.png" in Orthanc) + client.SetRedirectionFollowed(false); + + client.SetBody(body); + client.SetMethod(Orthanc::HttpMethod_Post); + + if (!client.Apply(result)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); + } + } +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Messaging/CurlOrthancConnection.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,68 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "IOrthancConnection.h" + +#if ORTHANC_ENABLE_CURL == 1 + +#include "../Orthanc/Core/WebServiceParameters.h" + +namespace OrthancStone +{ + class CurlOrthancConnection : public IOrthancConnection + { + private: + Orthanc::WebServiceParameters parameters_; + + public: + CurlOrthancConnection(const Orthanc::WebServiceParameters& parameters) : + parameters_(parameters) + { + } + + const Orthanc::WebServiceParameters& GetParameters() const + { + return parameters_; + } + + virtual void RestApiGet(std::string& result, + const std::string& uri); + + virtual void RestApiPost(std::string& result, + const std::string& uri, + const std::string& body); + }; +} + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Messaging/IOrthancConnection.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,52 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Toolbox/IThreadSafety.h" + +#include <string> + +namespace OrthancStone +{ + // Derived classes must be thread-safe + class IOrthancConnection : public IThreadSafe + { + public: + virtual void RestApiGet(std::string& result, + const std::string& uri) = 0; + + virtual void RestApiPost(std::string& result, + const std::string& uri, + const std::string& body) = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Messaging/MessagingToolbox.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,436 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "MessagingToolbox.h" + +#include "../Orthanc/Core/Images/Image.h" +#include "../Orthanc/Core/Images/ImageProcessing.h" +#include "../Orthanc/Core/Images/JpegReader.h" +#include "../Orthanc/Core/Images/PngReader.h" +#include "../Orthanc/Core/OrthancException.h" +#include "../Orthanc/Core/Toolbox.h" +#include "../Orthanc/Core/Logging.h" + +#include <boost/lexical_cast.hpp> +#include <json/reader.h> + +#if defined(__native_client__) +# include <boost/math/special_functions/round.hpp> +#else +# include <boost/date_time/microsec_time_clock.hpp> +#endif + +namespace OrthancStone +{ + namespace MessagingToolbox + { +#if defined(__native_client__) + static pp::Core* core_ = NULL; + + void Timestamp::Initialize(pp::Core* core) + { + if (core == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + core_ = core; + } + + Timestamp::Timestamp() + { + if (core_ == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + time_ = core_->GetTimeTicks(); + } + + int Timestamp::GetMillisecondsSince(const Timestamp& other) + { + double difference = time_ - other.time_; + return static_cast<int>(boost::math::iround(difference * 1000.0)); + } +#else + Timestamp::Timestamp() + { + time_ = boost::posix_time::microsec_clock::local_time(); + } + + int Timestamp::GetMillisecondsSince(const Timestamp& other) + { + boost::posix_time::time_duration difference = time_ - other.time_; + return static_cast<int>(difference.total_milliseconds()); + } +#endif + + static bool ParseVersion(std::string& version, + unsigned int& major, + unsigned int& minor, + unsigned int& patch, + const Json::Value& info) + { + if (info.type() != Json::objectValue || + !info.isMember("Version") || + info["Version"].type() != Json::stringValue) + { + return false; + } + + version = info["Version"].asString(); + if (version == "mainline") + { + // Some arbitrary high values Orthanc versions will never reach ;) + major = 999; + minor = 999; + patch = 999; + return true; + } + + std::vector<std::string> tokens; + Orthanc::Toolbox::TokenizeString(tokens, version, '.'); + + if (tokens.size() != 2 && + tokens.size() != 3) + { + return false; + } + + int a, b, c; + try + { + a = boost::lexical_cast<int>(tokens[0]); + b = boost::lexical_cast<int>(tokens[1]); + + if (tokens.size() == 3) + { + c = boost::lexical_cast<int>(tokens[2]); + } + else + { + c = 0; + } + } + catch (boost::bad_lexical_cast&) + { + return false; + } + + if (a < 0 || + b < 0 || + c < 0) + { + return false; + } + else + { + major = static_cast<unsigned int>(a); + minor = static_cast<unsigned int>(b); + patch = static_cast<unsigned int>(c); + return true; + } + } + + void ParseJson(Json::Value& target, + const std::string& source) + { + Json::Reader reader; + if (!reader.parse(source, target)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + void RestApiGet(Json::Value& target, + IOrthancConnection& orthanc, + const std::string& uri) + { + std::string tmp; + orthanc.RestApiGet(tmp, uri); + ParseJson(target, tmp); + } + + + bool HasWebViewerInstalled(IOrthancConnection& orthanc) + { + try + { + Json::Value json; + RestApiGet(json, orthanc, "/plugins/web-viewer"); + return json.type() == Json::objectValue; + } + catch (Orthanc::OrthancException&) + { + return false; + } + } + + + bool CheckOrthancVersion(IOrthancConnection& orthanc) + { + Json::Value json; + std::string version; + unsigned int major, minor, patch; + + try + { + RestApiGet(json, orthanc, "/system"); + } + catch (Orthanc::OrthancException&) + { + LOG(ERROR) << "Cannot connect to your Orthanc server"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + if (!ParseVersion(version, major, minor, patch, json)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + LOG(WARNING) << "Version of the Orthanc core (must be above 1.1.0): " << version; + + // Stone is only compatible with Orthanc >= 1.1.0, otherwise deadlocks might occur + if (major < 1 || + (major == 1 && minor < 1)) + { + return false; + } + + try + { + RestApiGet(json, orthanc, "/plugins/web-viewer"); + } + catch (Orthanc::OrthancException&) + { + // The Web viewer is not installed, this is OK + LOG(WARNING) << "The Web viewer plugin is not installed, progressive download is disabled"; + return true; + } + + if (!ParseVersion(version, major, minor, patch, json)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + LOG(WARNING) << "Version of the Web viewer plugin (must be above 2.2): " << version; + + return (major >= 3 || + (major == 2 && minor >= 2)); + } + + + Orthanc::ImageAccessor* DecodeFrame(IOrthancConnection& orthanc, + const std::string& instance, + unsigned int frame, + Orthanc::PixelFormat targetFormat) + { + std::string uri = ("instances/" + instance + "/frames/" + + boost::lexical_cast<std::string>(frame)); + + std::string compressed; + + switch (targetFormat) + { + case Orthanc::PixelFormat_RGB24: + orthanc.RestApiGet(compressed, uri + "/preview"); + break; + + case Orthanc::PixelFormat_Grayscale16: + orthanc.RestApiGet(compressed, uri + "/image-uint16"); + break; + + case Orthanc::PixelFormat_SignedGrayscale16: + orthanc.RestApiGet(compressed, uri + "/image-int16"); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::auto_ptr<Orthanc::PngReader> result(new Orthanc::PngReader); + result->ReadFromMemory(compressed); + + if (targetFormat == Orthanc::PixelFormat_SignedGrayscale16) + { + if (result->GetFormat() == Orthanc::PixelFormat_Grayscale16) + { + result->SetFormat(Orthanc::PixelFormat_SignedGrayscale16); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + } + + return result.release(); + } + + + Orthanc::ImageAccessor* DecodeJpegFrame(IOrthancConnection& orthanc, + const std::string& instance, + unsigned int frame, + unsigned int quality, + Orthanc::PixelFormat targetFormat) + { + if (quality <= 0 || + quality > 100) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + // This requires the official Web viewer plugin to be installed! + std::string uri = ("web-viewer/instances/jpeg" + + boost::lexical_cast<std::string>(quality) + + "-" + instance + "_" + + boost::lexical_cast<std::string>(frame)); + + Json::Value encoded; + RestApiGet(encoded, orthanc, uri); + + if (encoded.type() != Json::objectValue || + !encoded.isMember("Orthanc") || + encoded["Orthanc"].type() != Json::objectValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + Json::Value& info = encoded["Orthanc"]; + if (!info.isMember("PixelData") || + !info.isMember("Stretched") || + !info.isMember("Compression") || + info["Compression"].type() != Json::stringValue || + info["PixelData"].type() != Json::stringValue || + info["Stretched"].type() != Json::booleanValue || + info["Compression"].asString() != "Jpeg") + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + bool isSigned = false; + bool isStretched = info["Stretched"].asBool(); + + if (info.isMember("IsSigned")) + { + if (info["IsSigned"].type() != Json::booleanValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + else + { + isSigned = info["IsSigned"].asBool(); + } + } + + std::string jpeg; + Orthanc::Toolbox::DecodeBase64(jpeg, info["PixelData"].asString()); + + std::auto_ptr<Orthanc::JpegReader> reader(new Orthanc::JpegReader); + reader->ReadFromMemory(jpeg); + + if (reader->GetFormat() == Orthanc::PixelFormat_RGB24) // This is a color image + { + if (targetFormat != Orthanc::PixelFormat_RGB24) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + if (isSigned || isStretched) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + else + { + return reader.release(); + } + } + + if (reader->GetFormat() != Orthanc::PixelFormat_Grayscale8) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + if (!isStretched) + { + if (targetFormat != reader->GetFormat()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + return reader.release(); + } + + int32_t stretchLow = 0; + int32_t stretchHigh = 0; + + if (!info.isMember("StretchLow") || + !info.isMember("StretchHigh") || + info["StretchLow"].type() != Json::intValue || + info["StretchHigh"].type() != Json::intValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + stretchLow = info["StretchLow"].asInt(); + stretchHigh = info["StretchHigh"].asInt(); + + if (stretchLow < -32768 || + stretchHigh > 65535 || + (stretchLow < 0 && stretchHigh > 32767)) + { + // This range cannot be represented with a uint16_t or an int16_t + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + // Decode a grayscale JPEG 8bpp image coming from the Web viewer + std::auto_ptr<Orthanc::ImageAccessor> image + (new Orthanc::Image(targetFormat, reader->GetWidth(), reader->GetHeight())); + + float scaling = static_cast<float>(stretchHigh - stretchLow) / 255.0f; + float offset = static_cast<float>(stretchLow) / scaling; + + Orthanc::ImageProcessing::Convert(*image, *reader); + Orthanc::ImageProcessing::ShiftScale(*image, offset, scaling); + +#if 0 + /*info.removeMember("PixelData"); + std::cout << info.toStyledString();*/ + + int64_t a, b; + Orthanc::ImageProcessing::GetMinMaxValue(a, b, *image); + std::cout << stretchLow << "->" << stretchHigh << " = " << a << "->" << b << std::endl; +#endif + + return image.release(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Messaging/MessagingToolbox.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,97 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "IOrthancConnection.h" + +#include "../Enumerations.h" +#include "../Orthanc/Core/Images/ImageAccessor.h" + +#include <json/value.h> + +#if defined(__native_client__) +# include <ppapi/cpp/core.h> +#else +# include <boost/date_time/posix_time/ptime.hpp> +#endif + +namespace OrthancStone +{ + namespace MessagingToolbox + { + class Timestamp + { + private: +#if defined(__native_client__) + PP_TimeTicks time_; +#else + boost::posix_time::ptime time_; +#endif + + public: + Timestamp(); + +#if defined(__native_client__) + static void Initialize(pp::Core* core); +#endif + + int GetMillisecondsSince(const Timestamp& other); + }; + + + void ParseJson(Json::Value& target, + const std::string& source); + + void RestApiGet(Json::Value& target, + IOrthancConnection& orthanc, + const std::string& uri); + + bool HasWebViewerInstalled(IOrthancConnection& orthanc); + + bool CheckOrthancVersion(IOrthancConnection& orthanc); + + // This downloads the image from Orthanc and keeps its pixel + // format unchanged (will be either Grayscale8, Grayscale16, + // SignedGrayscale16, or RGB24) + Orthanc::ImageAccessor* DecodeFrame(IOrthancConnection& orthanc, + const std::string& instance, + unsigned int frame, + Orthanc::PixelFormat targetFormat); + + Orthanc::ImageAccessor* DecodeJpegFrame(IOrthancConnection& orthanc, + const std::string& instance, + unsigned int frame, + unsigned int quality, + Orthanc::PixelFormat targetFormat); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Orthanc/README.txt Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,3 @@ +This folder contains an excerpt of the source code of Orthanc. It is +automatically generated using the "../Resources/SyncOrthancFolder.py" +script.
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/BinarySemaphore.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,61 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "BinarySemaphore.h" + +namespace OrthancStone +{ + BinarySemaphore::BinarySemaphore() : + proceed_(false) + { + } + + void BinarySemaphore::Signal() + { + //boost::mutex::scoped_lock lock(mutex_); + + proceed_ = true; + condition_.notify_one(); + } + + void BinarySemaphore::Wait() + { + boost::mutex::scoped_lock lock(mutex_); + + while (!proceed_) + { + condition_.wait(lock); + } + + proceed_ = false; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/BinarySemaphore.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,54 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <boost/thread/mutex.hpp> +#include <boost/thread/condition.hpp> + +namespace OrthancStone +{ + class BinarySemaphore : public boost::noncopyable + { + private: + bool proceed_; + boost::mutex mutex_; + boost::condition_variable condition_; + + public: + explicit BinarySemaphore(); + + void Signal(); + + void Wait(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/DicomDataset.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,315 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "DicomDataset.h" + +#include "../Orthanc/Core/OrthancException.h" +#include "../Orthanc/Core/Logging.h" +#include "../Orthanc/Core/Toolbox.h" + +#include <boost/lexical_cast.hpp> +#include <json/value.h> +#include <json/reader.h> + +namespace OrthancStone +{ + static uint16_t GetCharValue(char c) + { + if (c >= '0' && c <= '9') + return c - '0'; + else if (c >= 'a' && c <= 'f') + return c - 'a' + 10; + else if (c >= 'A' && c <= 'F') + return c - 'A' + 10; + else + return 0; + } + + + static uint16_t GetHexadecimalValue(const char* c) + { + return ((GetCharValue(c[0]) << 12) + + (GetCharValue(c[1]) << 8) + + (GetCharValue(c[2]) << 4) + + GetCharValue(c[3])); + } + + + static DicomDataset::Tag ParseTag(const std::string& tag) + { + if (tag.size() == 9 && + isxdigit(tag[0]) && + isxdigit(tag[1]) && + isxdigit(tag[2]) && + isxdigit(tag[3]) && + (tag[4] == '-' || tag[4] == ',') && + isxdigit(tag[5]) && + isxdigit(tag[6]) && + isxdigit(tag[7]) && + isxdigit(tag[8])) + { + uint16_t group = GetHexadecimalValue(tag.c_str()); + uint16_t element = GetHexadecimalValue(tag.c_str() + 5); + return std::make_pair(group, element); + } + else if (tag.size() == 8 && + isxdigit(tag[0]) && + isxdigit(tag[1]) && + isxdigit(tag[2]) && + isxdigit(tag[3]) && + isxdigit(tag[4]) && + isxdigit(tag[5]) && + isxdigit(tag[6]) && + isxdigit(tag[7])) + { + uint16_t group = GetHexadecimalValue(tag.c_str()); + uint16_t element = GetHexadecimalValue(tag.c_str() + 4); + return std::make_pair(group, element); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + void DicomDataset::Parse(const std::string& content) + { + Json::Value json; + Json::Reader reader; + if (!reader.parse(content, json)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + Parse(json); + } + + + void DicomDataset::Parse(const Json::Value& content) + { + if (content.type() != Json::objectValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + Json::Value::Members members = content.getMemberNames(); + for (size_t i = 0; i < members.size(); i++) + { + Tag tag = ParseTag(members[i]); + + const Json::Value& item = content[members[i]]; + + if (item.type() != Json::objectValue || + !item.isMember("Type") || + !item.isMember("Value") || + !item.isMember("Name") || + item["Type"].type() != Json::stringValue || + item["Name"].type() != Json::stringValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + if (item["Type"].asString() == "String") + { + if (item["Value"].type() == Json::stringValue) + { + values_[tag] = item["Value"].asString(); + } + else + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + } + } + + + DicomDataset::DicomDataset(IOrthancConnection& orthanc, + const std::string& instanceId) + { + std::string content; + orthanc.RestApiGet(content, "/instances/" + instanceId + "/tags"); + + Parse(content); + } + + + std::string DicomDataset::GetStringValue(const Tag& tag) const + { + Values::const_iterator it = values_.find(tag); + + if (it == values_.end()) + { + LOG(ERROR) << "Trying to access a DICOM tag that is not set in a DICOM dataset"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentItem); + } + else + { + return it->second; + } + } + + + std::string DicomDataset::GetStringValue(const Tag& tag, + const std::string& defaultValue) const + { + Values::const_iterator it = values_.find(tag); + + if (it == values_.end()) + { + return defaultValue; + } + else + { + return it->second; + } + } + + + float DicomDataset::GetFloatValue(const Tag& tag) const + { + try + { + return boost::lexical_cast<float>(Orthanc::Toolbox::StripSpaces(GetStringValue(tag))); + } + catch (boost::bad_lexical_cast&) + { + LOG(ERROR) << "Trying to access a DICOM tag that is not a float"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + + double DicomDataset::GetDoubleValue(const Tag& tag) const + { + try + { + return boost::lexical_cast<double>(Orthanc::Toolbox::StripSpaces(GetStringValue(tag))); + } + catch (boost::bad_lexical_cast&) + { + LOG(ERROR) << "Trying to access a DICOM tag that is not a float"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + + int DicomDataset::GetIntegerValue(const Tag& tag) const + { + try + { + return boost::lexical_cast<int>(Orthanc::Toolbox::StripSpaces(GetStringValue(tag))); + } + catch (boost::bad_lexical_cast&) + { + LOG(ERROR) << "Trying to access a DICOM tag that is not an integer"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + + unsigned int DicomDataset::GetUnsignedIntegerValue(const Tag& tag) const + { + int v = GetIntegerValue(tag); + + if (v < 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + return static_cast<unsigned int>(v); + } + } + + + void DicomDataset::GetVectorValue(Vector& vector, + const Tag& tag) const + { + if (!GeometryToolbox::ParseVector(vector, Orthanc::Toolbox::StripSpaces(GetStringValue(tag)))) + { + LOG(ERROR) << "Trying to access a DICOM tag that is not a vector"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + + void DicomDataset::GetVectorValue(Vector& vector, + const Tag& tag, + size_t expectedSize) const + { + GetVectorValue(vector, tag); + + if (vector.size() != expectedSize) + { + LOG(ERROR) << "A vector in a DICOM tag has a bad size"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + + void DicomDataset::Print() const + { + for (Values::const_iterator it = values_.begin(); it != values_.end(); ++it) + { + printf("%04x,%04x = [%s]\n", it->first.first, it->first.second, it->second.c_str()); + } + printf("\n"); + } + + + bool DicomDataset::IsGrayscale() const + { + std::string photometric = Orthanc::Toolbox::StripSpaces(GetStringValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION)); + + return (photometric == "MONOCHROME1" || + photometric == "MONOCHROME2"); + } + + + void DicomDataset::GetPixelSpacing(double& spacingX, + double& spacingY) const + { + if (HasTag(DICOM_TAG_PIXEL_SPACING)) + { + Vector spacing; + GetVectorValue(spacing, DICOM_TAG_PIXEL_SPACING, 2); + spacingX = spacing[0]; + spacingY = spacing[1]; + } + else + { + spacingX = 1.0; + spacingY = 1.0; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/DicomDataset.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,121 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "GeometryToolbox.h" +#include "../Messaging/IOrthancConnection.h" + +#include <map> +#include <stdint.h> +#include <json/value.h> + +namespace OrthancStone +{ + // This class is NOT thread-safe + // This is a lightweight alternative to Orthanc::DicomMap + class DicomDataset : public boost::noncopyable + { + public: + typedef std::pair<uint16_t, uint16_t> Tag; + + private: + typedef std::map<Tag, std::string> Values; + + Values values_; + + void Parse(const std::string& content); + + void Parse(const Json::Value& content); + + public: + DicomDataset(const std::string& content) + { + Parse(content); + } + + DicomDataset(const Json::Value& content) + { + Parse(content); + } + + DicomDataset(IOrthancConnection& orthanc, + const std::string& instanceId); + + bool HasTag(const Tag& tag) const + { + return values_.find(tag) != values_.end(); + } + + std::string GetStringValue(const Tag& tag) const; + + std::string GetStringValue(const Tag& tag, + const std::string& defaultValue) const; + + float GetFloatValue(const Tag& tag) const; + + double GetDoubleValue(const Tag& tag) const; + + int GetIntegerValue(const Tag& tag) const; + + unsigned int GetUnsignedIntegerValue(const Tag& tag) const; + + void GetVectorValue(Vector& vector, + const Tag& tag, + size_t expectedSize) const; + + void GetVectorValue(Vector& vector, + const Tag& tag) const; + + void Print() const; + + bool IsGrayscale() const; + + void GetPixelSpacing(double& spacingX, + double& spacingY) const; + }; + + + static const DicomDataset::Tag DICOM_TAG_COLUMNS(0x0028, 0x0011); + static const DicomDataset::Tag DICOM_TAG_IMAGE_ORIENTATION_PATIENT(0x0020, 0x0037); + static const DicomDataset::Tag DICOM_TAG_IMAGE_POSITION_PATIENT(0x0020, 0x0032); + static const DicomDataset::Tag DICOM_TAG_NUMBER_OF_FRAMES(0x0028, 0x0008); + static const DicomDataset::Tag DICOM_TAG_PIXEL_REPRESENTATION(0x0028, 0x0103); + static const DicomDataset::Tag DICOM_TAG_PIXEL_SPACING(0x0028, 0x0030); + static const DicomDataset::Tag DICOM_TAG_RESCALE_INTERCEPT(0x0028, 0x1052); + static const DicomDataset::Tag DICOM_TAG_RESCALE_SLOPE(0x0028, 0x1053); + static const DicomDataset::Tag DICOM_TAG_ROWS(0x0028, 0x0010); + static const DicomDataset::Tag DICOM_TAG_SLICE_THICKNESS(0x0018, 0x0050); + static const DicomDataset::Tag DICOM_TAG_WINDOW_CENTER(0x0028, 0x1050); + static const DicomDataset::Tag DICOM_TAG_WINDOW_WIDTH(0x0028, 0x1051); + static const DicomDataset::Tag DICOM_TAG_PHOTOMETRIC_INTERPRETATION(0x0028, 0x0004); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/DicomFrameConverter.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,162 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "DicomFrameConverter.h" + +#include "../Orthanc/Core/Images/Image.h" +#include "../Orthanc/Core/Images/ImageProcessing.h" +#include "../Orthanc/Core/OrthancException.h" +#include "../Orthanc/Core/Toolbox.h" + +namespace OrthancStone +{ + void DicomFrameConverter::SetDefaultParameters() + { + isSigned_ = true; + isColor_ = false; + hasRescale_ = false; + rescaleIntercept_ = 0; + rescaleSlope_ = 1; + defaultWindowCenter_ = 128; + defaultWindowWidth_ = 256; + } + + + Orthanc::PixelFormat DicomFrameConverter::GetExpectedPixelFormat() const + { + // TODO Add more checks, e.g. on the number of bytes per value + // (cf. DicomImageInformation.h in Orthanc) + + if (isColor_) + { + return Orthanc::PixelFormat_RGB24; + } + else if (isSigned_) + { + return Orthanc::PixelFormat_SignedGrayscale16; + } + else + { + return Orthanc::PixelFormat_Grayscale16; + } + } + + + void DicomFrameConverter::ReadParameters(const DicomDataset& dicom) + { + SetDefaultParameters(); + + if (dicom.HasTag(DICOM_TAG_WINDOW_CENTER)) + { + Vector c, w; + dicom.GetVectorValue(c, DICOM_TAG_WINDOW_CENTER); + dicom.GetVectorValue(w, DICOM_TAG_WINDOW_WIDTH); + + if (c.size() > 0 && w.size() > 0) + { + defaultWindowCenter_ = static_cast<float>(c[0]); + defaultWindowWidth_ = static_cast<float>(w[0]); + } + } + + isSigned_ = (dicom.GetIntegerValue(DICOM_TAG_PIXEL_REPRESENTATION) == 1); // Type 1 tag, must be present + + if (dicom.HasTag(DICOM_TAG_RESCALE_INTERCEPT)) + { + rescaleIntercept_ = dicom.GetFloatValue(DICOM_TAG_RESCALE_INTERCEPT); + rescaleSlope_ = dicom.GetFloatValue(DICOM_TAG_RESCALE_SLOPE); + hasRescale_ = true; + } + + std::string photometric = dicom.GetStringValue(DICOM_TAG_PHOTOMETRIC_INTERPRETATION); // Type 1 tag, must be present + photometric = Orthanc::Toolbox::StripSpaces(photometric); + isColor_ = (photometric != "MONOCHROME1" && + photometric != "MONOCHROME2"); + } + + + void DicomFrameConverter::ConvertFrame(std::auto_ptr<Orthanc::ImageAccessor>& source) const + { + assert(sizeof(float) == 4); + + if (source.get() == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + Orthanc::PixelFormat sourceFormat = source->GetFormat(); + + if (sourceFormat != GetExpectedPixelFormat()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + if (sourceFormat == Orthanc::PixelFormat_RGB24) + { + // No conversion has to be done + return; + } + + assert(sourceFormat == Orthanc::PixelFormat_Grayscale16 || + sourceFormat == Orthanc::PixelFormat_SignedGrayscale16); + + // This is the case of a grayscale frame. Convert it to Float32. + std::auto_ptr<Orthanc::Image> converted(new Orthanc::Image(Orthanc::PixelFormat_Float32, + source->GetWidth(), + source->GetHeight())); + Orthanc::ImageProcessing::Convert(*converted, *source); + + source.reset(NULL); // We don't need the source frame anymore + + // Correct rescale slope/intercept if need be + if (hasRescale_) + { + for (unsigned int y = 0; y < converted->GetHeight(); y++) + { + float* p = reinterpret_cast<float*>(converted->GetRow(y)); + for (unsigned int x = 0; x < converted->GetWidth(); x++, p++) + { + float value = *p; + + if (hasRescale_) + { + value = value * rescaleSlope_ + rescaleIntercept_; + } + + *p = value; + } + } + } + + source = converted; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/DicomFrameConverter.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,83 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "DicomDataset.h" + +#include "../Orthanc/Core/Images/ImageAccessor.h" + +namespace OrthancStone +{ + /** + * This class is responsible for converting the pixel format of a + * DICOM frame coming from Orthanc, into a pixel format that is + * suitable for Stone, given the relevant DICOM tags: + * - Color frames will stay in the RGB24 format. + * - Grayscale frames will be converted to the Float32 format. + **/ + class DicomFrameConverter + { + private: + bool isSigned_; + bool isColor_; + bool hasRescale_; + float rescaleIntercept_; + float rescaleSlope_; + float defaultWindowCenter_; + float defaultWindowWidth_; + + void SetDefaultParameters(); + + public: + DicomFrameConverter() + { + SetDefaultParameters(); + } + + Orthanc::PixelFormat GetExpectedPixelFormat() const; + + void ReadParameters(const DicomDataset& dicom); + + float GetDefaultWindowCenter() const + { + return defaultWindowCenter_; + } + + float GetDefaultWindowWidth() const + { + return defaultWindowWidth_; + } + + void ConvertFrame(std::auto_ptr<Orthanc::ImageAccessor>& source) const; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/DicomStructureSet.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,399 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "DicomStructureSet.h" + +#include "../Orthanc/Core/Logging.h" +#include "../Orthanc/Core/OrthancException.h" +#include "../Messaging/MessagingToolbox.h" + +#include <stdio.h> +#include <boost/lexical_cast.hpp> + +namespace OrthancStone +{ + static const Json::Value& GetSequence(const Json::Value& instance, + uint16_t group, + uint16_t element) + { + char buf[16]; + sprintf(buf, "%04x,%04x", group, element); + + if (instance.type() != Json::objectValue || + !instance.isMember(buf) || + instance[buf].type() != Json::objectValue || + !instance[buf].isMember("Type") || + !instance[buf].isMember("Value") || + instance[buf]["Type"].type() != Json::stringValue || + instance[buf]["Value"].type() != Json::arrayValue || + instance[buf]["Type"].asString() != "Sequence") + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + return instance[buf]["Value"]; + } + + + static uint8_t ConvertColor(double v) + { + if (v < 0) + { + return 0; + } + else if (v >= 255) + { + return 255; + } + else + { + return static_cast<uint8_t>(v); + } + } + + + SliceGeometry DicomStructureSet::ExtractSliceGeometry(double& sliceThickness, + IOrthancConnection& orthanc, + const Json::Value& contour) + { + const Json::Value& sequence = GetSequence(contour, 0x3006, 0x0016); + + if (sequence.size() != 1) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + DicomDataset contourImageSequence(sequence[0]); + std::string parentUid = contourImageSequence.GetStringValue(std::make_pair(0x0008, 0x1155)); + + std::string post; + orthanc.RestApiPost(post, "/tools/lookup", parentUid); + + Json::Value tmp; + MessagingToolbox::ParseJson(tmp, post); + + if (tmp.type() != Json::arrayValue || + tmp.size() != 1 || + !tmp[0].isMember("Type") || + !tmp[0].isMember("Path") || + tmp[0]["Type"].type() != Json::stringValue || + tmp[0]["ID"].type() != Json::stringValue || + tmp[0]["Type"].asString() != "Instance") + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); + } + + Json::Value parentInstance; + MessagingToolbox::RestApiGet(parentInstance, orthanc, "/instances/" + tmp[0]["ID"].asString()); + + if (parentInstance.type() != Json::objectValue || + !parentInstance.isMember("ParentSeries") || + parentInstance["ParentSeries"].type() != Json::stringValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + std::string parentSeriesId = parentInstance["ParentSeries"].asString(); + bool isFirst = parentSeriesId_.empty(); + + if (isFirst) + { + parentSeriesId_ = parentSeriesId; + } + else if (parentSeriesId_ != parentSeriesId) + { + LOG(ERROR) << "This RT-STRUCT refers to several different series"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + Json::Value parentTags; + MessagingToolbox::RestApiGet(parentTags, orthanc, "/instances/" + tmp[0]["ID"].asString() + "/tags?simplify"); + + if (parentTags.type() != Json::objectValue || + !parentTags.isMember("ImageOrientationPatient") || + !parentTags.isMember("ImagePositionPatient") || + parentTags["ImageOrientationPatient"].type() != Json::stringValue || + parentTags["ImagePositionPatient"].type() != Json::stringValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NetworkProtocol); + } + + SliceGeometry slice(parentTags["ImagePositionPatient"].asString(), + parentTags["ImageOrientationPatient"].asString()); + + sliceThickness = 1; // 1 mm by default + + if (parentTags.isMember("SliceThickness") && + parentTags["SliceThickness"].type() == Json::stringValue) + { + Vector tmp; + GeometryToolbox::ParseVector(tmp, parentTags["SliceThickness"].asString()); + if (tmp.size() > 0) + { + sliceThickness = tmp[0]; + } + } + + if (isFirst) + { + normal_ = slice.GetNormal(); + } + else if (!GeometryToolbox::IsParallel(normal_, slice.GetNormal())) + { + LOG(ERROR) << "Incompatible orientation of slices in this RT-STRUCT"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + return slice; + } + + + const DicomStructureSet::Structure& DicomStructureSet::GetStructure(size_t index) const + { + if (index >= structures_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + return structures_[index]; + } + + + bool DicomStructureSet::IsPolygonOnSlice(const Polygon& polygon, + const SliceGeometry& geometry) const + { + double d = boost::numeric::ublas::inner_prod(geometry.GetOrigin(), normal_); + + return (GeometryToolbox::IsNear(d, polygon.projectionAlongNormal_, polygon.sliceThickness_ / 2.0) && + !polygon.points_.empty()); + } + + + DicomStructureSet::DicomStructureSet(IOrthancConnection& orthanc, + const std::string& instanceId) + { + Json::Value instance; + MessagingToolbox::RestApiGet(instance, orthanc, "/instances/" + instanceId + "/tags"); + + Json::Value rtRoiObservationSequence = GetSequence(instance, 0x3006, 0x0080); + Json::Value roiContourSequence = GetSequence(instance, 0x3006, 0x0039); + Json::Value structureSetRoiSequence = GetSequence(instance, 0x3006, 0x0020); + + Json::Value::ArrayIndex count = rtRoiObservationSequence.size(); + if (count != roiContourSequence.size() || + count != structureSetRoiSequence.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + structures_.resize(count); + for (Json::Value::ArrayIndex i = 0; i < count; i++) + { + DicomDataset observation(rtRoiObservationSequence[i]); + DicomDataset roi(structureSetRoiSequence[i]); + DicomDataset content(roiContourSequence[i]); + + structures_[i].interpretation_ = observation.GetStringValue(std::make_pair(0x3006, 0x00a4), "No interpretation"); + structures_[i].name_ = roi.GetStringValue(std::make_pair(0x3006, 0x0026), "No name"); + structures_[i].red_ = 255; + structures_[i].green_ = 0; + structures_[i].blue_ = 0; + + DicomDataset::Tag tag(0x3006, 0x002a); + + Vector color; + if (content.HasTag(tag)) + { + content.GetVectorValue(color, tag); + if (color.size() == 3) + { + structures_[i].red_ = ConvertColor(color[0]); + structures_[i].green_ = ConvertColor(color[1]); + structures_[i].blue_ = ConvertColor(color[2]); + } + } + + const Json::Value& slices = GetSequence(roiContourSequence[i], 0x3006, 0x0040); + + LOG(WARNING) << "New RT structure: \"" << structures_[i].name_ + << "\" with interpretation \"" << structures_[i].interpretation_ + << "\" containing " << slices.size() << " slices (color: " + << static_cast<int>(structures_[i].red_) << "," + << static_cast<int>(structures_[i].green_) << "," + << static_cast<int>(structures_[i].blue_) << ")"; + + for (Json::Value::ArrayIndex j = 0; j < slices.size(); j++) + { + DicomDataset slice(slices[j]); + + unsigned int npoints = slice.GetUnsignedIntegerValue(std::make_pair(0x3006, 0x0046)); + LOG(INFO) << "Parsing slice containing " << npoints << " vertices"; + + if (slice.GetStringValue(std::make_pair(0x3006, 0x0042)) != "CLOSED_PLANAR") + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + std::string slicesData; + orthanc.RestApiGet(slicesData, "/instances/" + instanceId + "/content/3006-0039/" + + boost::lexical_cast<std::string>(i) + "/3006-0040/" + + boost::lexical_cast<std::string>(j) + "/3006-0050"); + + Vector points; + if (!GeometryToolbox::ParseVector(points, slicesData) || + points.size() != 3 * npoints) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + Polygon polygon; + SliceGeometry geometry = ExtractSliceGeometry(polygon.sliceThickness_, orthanc, slices[j]); + polygon.projectionAlongNormal_ = geometry.ProjectAlongNormal(geometry.GetOrigin()); + + for (size_t k = 0; k < npoints; k++) + { + Vector v(3); + v[0] = points[3 * k]; + v[1] = points[3 * k + 1]; + v[2] = points[3 * k + 2]; + + if (!GeometryToolbox::IsNear(geometry.ProjectAlongNormal(v), + polygon.projectionAlongNormal_, + polygon.sliceThickness_ / 2.0 /* in mm */)) + { + LOG(ERROR) << "This RT-STRUCT contains a point that is off the slice of its instance"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + polygon.points_.push_back(v); + } + + structures_[i].polygons_.push_back(polygon); + } + } + + if (parentSeriesId_.empty() || + normal_.size() != 3) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + + Vector DicomStructureSet::GetStructureCenter(size_t index) const + { + const Structure& structure = GetStructure(index); + + Vector center; + GeometryToolbox::AssignVector(center, 0, 0, 0); + if (structure.polygons_.empty()) + { + return center; + } + + double n = static_cast<double>(structure.polygons_.size()); + + for (Polygons::const_iterator polygon = structure.polygons_.begin(); + polygon != structure.polygons_.end(); ++polygon) + { + if (!polygon->points_.empty()) + { + center += polygon->points_.front() / n; + } + } + + return center; + } + + + const std::string& DicomStructureSet::GetStructureName(size_t index) const + { + return GetStructure(index).name_; + } + + + const std::string& DicomStructureSet::GetStructureInterpretation(size_t index) const + { + return GetStructure(index).interpretation_; + } + + + void DicomStructureSet::GetStructureColor(uint8_t& red, + uint8_t& green, + uint8_t& blue, + size_t index) const + { + const Structure& s = GetStructure(index); + red = s.red_; + green = s.green_; + blue = s.blue_; + } + + + void DicomStructureSet::Render(CairoContext& context, + const SliceGeometry& slice) const + { + cairo_t* cr = context.GetObject(); + + for (Structures::const_iterator structure = structures_.begin(); + structure != structures_.end(); ++structure) + { + for (Polygons::const_iterator polygon = structure->polygons_.begin(); + polygon != structure->polygons_.end(); ++polygon) + { + if (IsPolygonOnSlice(*polygon, slice)) + { + context.SetSourceColor(structure->red_, structure->green_, structure->blue_); + + Points::const_iterator p = polygon->points_.begin(); + + double x, y; + slice.ProjectPoint(x, y, *p); + cairo_move_to(cr, x, y); + ++p; + + while (p != polygon->points_.end()) + { + slice.ProjectPoint(x, y, *p); + cairo_line_to(cr, x, y); + ++p; + } + + slice.ProjectPoint(x, y, *polygon->points_.begin()); + cairo_line_to(cr, x, y); + } + } + } + + cairo_stroke(cr); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/DicomStructureSet.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,110 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SliceGeometry.h" +#include "../Viewport/CairoContext.h" + +#include <list> + +namespace OrthancStone +{ + class DicomStructureSet : public boost::noncopyable + { + private: + typedef std::list<Vector> Points; + + struct Polygon + { + double projectionAlongNormal_; + double sliceThickness_; // In millimeters + Points points_; + }; + + typedef std::list<Polygon> Polygons; + + struct Structure + { + std::string name_; + std::string interpretation_; + Polygons polygons_; + uint8_t red_; + uint8_t green_; + uint8_t blue_; + }; + + typedef std::vector<Structure> Structures; + + Structures structures_; + std::string parentSeriesId_; + Vector normal_; + + SliceGeometry ExtractSliceGeometry(double& sliceThickness, + IOrthancConnection& orthanc, + const Json::Value& contour); + + const Structure& GetStructure(size_t index) const; + + bool IsPolygonOnSlice(const Polygon& polygon, + const SliceGeometry& geometry) const; + + + public: + DicomStructureSet(IOrthancConnection& orthanc, + const std::string& instanceId); + + size_t GetStructureCount() const + { + return structures_.size(); + } + + Vector GetStructureCenter(size_t index) const; + + const std::string& GetStructureName(size_t index) const; + + const std::string& GetStructureInterpretation(size_t index) const; + + void GetStructureColor(uint8_t& red, + uint8_t& green, + uint8_t& blue, + size_t index) const; + + const Vector& GetNormal() const + { + return normal_; + } + + void Render(CairoContext& context, + const SliceGeometry& slice) const; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/DownloadStack.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,209 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "DownloadStack.h" + +#include "../Orthanc/Core/OrthancException.h" + +#include <cassert> + +namespace OrthancStone +{ + bool DownloadStack::CheckInvariants() const + { + std::vector<bool> dequeued(nodes_.size(), true); + + int i = firstNode_; + while (i != NIL) + { + const Node& node = nodes_[i]; + + dequeued[i] = false; + + if (node.next_ != NIL && + nodes_[node.next_].prev_ != i) + { + return false; + } + + if (node.prev_ != NIL && + nodes_[node.prev_].next_ != i) + { + return false; + } + + i = nodes_[i].next_; + } + + for (size_t i = 0; i < nodes_.size(); i++) + { + if (nodes_[i].dequeued_ != dequeued[i]) + { + return false; + } + } + + return true; + } + + + DownloadStack::DownloadStack(unsigned int size) + { + nodes_.resize(size); + + if (size == 0) + { + firstNode_ = NIL; + } + else + { + for (size_t i = 0; i < size; i++) + { + nodes_[i].prev_ = i - 1; + nodes_[i].next_ = i + 1; + nodes_[i].dequeued_ = false; + } + + nodes_.front().prev_ = NIL; + nodes_.back().next_ = NIL; + firstNode_ = 0; + } + + assert(CheckInvariants()); + } + + + DownloadStack::~DownloadStack() + { + assert(CheckInvariants()); + } + + + bool DownloadStack::Pop(unsigned int& value) + { + boost::mutex::scoped_lock lock(mutex_); + + assert(CheckInvariants()); + + if (firstNode_ == NIL) + { + for (size_t i = 0; i < nodes_.size(); i++) + { + assert(nodes_[i].dequeued_); + } + + return false; + } + else + { + assert(firstNode_ >= 0 && firstNode_ < static_cast<int>(nodes_.size())); + value = firstNode_; + + Node& node = nodes_[firstNode_]; + assert(node.prev_ == NIL); + assert(!node.dequeued_); + + node.dequeued_ = true; + firstNode_ = node.next_; + + if (firstNode_ != NIL) + { + nodes_[firstNode_].prev_ = NIL; + } + + return true; + } + } + + + void DownloadStack::SetTopNodeInternal(unsigned int value) + { + assert(CheckInvariants()); + + Node& node = nodes_[value]; + + if (node.dequeued_) + { + // This node has already been processed by the download thread, nothing to do + return; + } + + // Remove the node from the list + if (node.prev_ == NIL) + { + assert(firstNode_ == static_cast<int>(value)); + + // This is already the top node in the list, nothing to do + return; + } + + nodes_[node.prev_].next_ = node.next_; + + if (node.next_ != NIL) + { + nodes_[node.next_].prev_ = node.prev_; + } + + // Add back the node at the top of the list + assert(firstNode_ != NIL); + + Node& old = nodes_[firstNode_]; + assert(old.prev_ == NIL); + assert(!old.dequeued_); + node.prev_ = NIL; + node.next_ = firstNode_; + old.prev_ = value; + + firstNode_ = value; + } + + + void DownloadStack::Writer::SetTopNode(unsigned int value) + { + if (value >= that_.nodes_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + that_.SetTopNodeInternal(value); + } + + + void DownloadStack::Writer::SetTopNodePermissive(int value) + { + if (value >= 0 && + value < static_cast<int>(that_.nodes_.size())) + { + that_.SetTopNodeInternal(value); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/DownloadStack.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,87 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <vector> +#include <boost/noncopyable.hpp> +#include <boost/thread/mutex.hpp> + +namespace OrthancStone +{ + class DownloadStack : public boost::noncopyable + { + private: + static const int NIL = -1; + + // This is a doubly-linked list + struct Node + { + int next_; + int prev_; + bool dequeued_; + }; + + boost::mutex mutex_; + std::vector<Node> nodes_; + int firstNode_; + + bool CheckInvariants() const; + + void SetTopNodeInternal(unsigned int value); + + public: + DownloadStack(unsigned int size); + + ~DownloadStack(); + + bool Pop(unsigned int& value); + + class Writer : public boost::noncopyable + { + private: + DownloadStack& that_; + boost::mutex::scoped_lock lock_; + + public: + Writer(DownloadStack& that) : + that_(that), + lock_(that.mutex_) + { + } + + void SetTopNode(unsigned int value); + + void SetTopNodePermissive(int value); + }; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/GeometryToolbox.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,351 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "GeometryToolbox.h" + +#include "../Orthanc/Core/OrthancException.h" +#include "../Orthanc/Core/Toolbox.h" + +#include <stdio.h> +#include <boost/lexical_cast.hpp> + +namespace OrthancStone +{ + namespace GeometryToolbox + { + void Print(const Vector& v) + { + for (size_t i = 0; i < v.size(); i++) + { + printf("%8.2f\n", v[i]); + } + printf("\n"); + } + + + bool ParseVector(Vector& target, + const std::string& value) + { + std::vector<std::string> items; + Orthanc::Toolbox::TokenizeString(items, value, '\\'); + + target.resize(items.size()); + + for (size_t i = 0; i < items.size(); i++) + { + try + { + target[i] = boost::lexical_cast<double>(Orthanc::Toolbox::StripSpaces(items[i])); + } + catch (boost::bad_lexical_cast&) + { + target.clear(); + return false; + } + } + + return true; + } + + + void AssignVector(Vector& v, + double v1, + double v2) + { + v.resize(2); + v[0] = v1; + v[1] = v2; + } + + + void AssignVector(Vector& v, + double v1, + double v2, + double v3) + { + v.resize(3); + v[0] = v1; + v[1] = v2; + v[2] = v3; + } + + + bool IsNear(double x, + double y) + { + // As most input is read as single-precision numbers, we take the + // epsilon machine for float32 into consideration to compare numbers + return IsNear(x, y, 10.0 * std::numeric_limits<float>::epsilon()); + } + + + void NormalizeVector(Vector& u) + { + double norm = boost::numeric::ublas::norm_2(u); + if (!IsCloseToZero(norm)) + { + u = u / norm; + } + } + + + void CrossProduct(Vector& result, + const Vector& u, + const Vector& v) + { + if (u.size() != 3 || + v.size() != 3) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + result.resize(3); + + result[0] = u[1] * v[2] - u[2] * v[1]; + result[1] = u[2] * v[0] - u[0] * v[2]; + result[2] = u[0] * v[1] - u[1] * v[0]; + } + + + void ProjectPointOntoPlane(Vector& result, + const Vector& point, + const Vector& planeNormal, + const Vector& planeOrigin) + { + double norm = boost::numeric::ublas::norm_2(planeNormal); + if (IsCloseToZero(norm)) + { + // Division by zero + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + // Make sure the norm of the normal is 1 + Vector n; + n = planeNormal / norm; + + // Algebraic form of line–plane intersection, where the line passes + // through "point" along the direction "normal" (thus, l == n) + // https://en.wikipedia.org/wiki/Line%E2%80%93plane_intersection#Algebraic_form + result = boost::numeric::ublas::inner_prod(planeOrigin - point, n) * n + point; + } + + + + bool IsParallelOrOpposite(bool& isOpposite, + const Vector& u, + const Vector& v) + { + // The dot product of the two vectors gives the cosine of the angle + // between the vectors + // https://en.wikipedia.org/wiki/Dot_product + + double normU = boost::numeric::ublas::norm_2(u); + double normV = boost::numeric::ublas::norm_2(v); + + if (IsCloseToZero(normU) || + IsCloseToZero(normV)) + { + return false; + } + + double cosAngle = boost::numeric::ublas::inner_prod(u, v) / (normU * normV); + + // The angle must be zero, so the cosine must be almost equal to + // cos(0) == 1 (or cos(180) == -1 if allowOppositeDirection == true) + + if (IsCloseToZero(cosAngle - 1.0)) + { + isOpposite = false; + return true; + } + else if (IsCloseToZero(fabs(cosAngle) - 1.0)) + { + isOpposite = true; + return true; + } + else + { + return false; + } + } + + + bool IsParallel(const Vector& u, + const Vector& v) + { + bool isOpposite; + return (IsParallelOrOpposite(isOpposite, u, v) && + !isOpposite); + } + + + bool IntersectTwoPlanes(Vector& p, + Vector& direction, + const Vector& origin1, + const Vector& normal1, + const Vector& origin2, + const Vector& normal2) + { + // This is "Intersection of 2 Planes", possibility "(C) 3 Plane + // Intersect Point" of: + // http://geomalgorithms.com/a05-_intersect-1.html + + // The direction of the line of intersection is orthogonal to the + // normal of both planes + CrossProduct(direction, normal1, normal2); + + double norm = boost::numeric::ublas::norm_2(direction); + if (IsCloseToZero(norm)) + { + // The two planes are parallel or coincident + return false; + } + + double d1 = -boost::numeric::ublas::inner_prod(normal1, origin1); + double d2 = -boost::numeric::ublas::inner_prod(normal2, origin2); + Vector tmp = d2 * normal1 - d1 * normal2; + + CrossProduct(p, tmp, direction); + p /= norm; + + return true; + } + + + bool ClipLineToRectangle(double& x1, // Coordinates of the clipped line (out) + double& y1, + double& x2, + double& y2, + const double ax, // Two points defining the line (in) + const double ay, + const double bx, + const double by, + const double& xmin, // Coordinates of the rectangle (in) + const double& ymin, + const double& xmax, + const double& ymax) + { + // This is Skala algorithm for rectangles, "A new approach to line + // and line segment clipping in homogeneous coordinates" + // (2005). This is a direct, non-optimized translation of Algorithm + // 2 in the paper. + + static uint8_t tab1[16] = { 255 /* none */, + 0, + 0, + 1, + 1, + 255 /* na */, + 0, + 2, + 2, + 0, + 255 /* na */, + 1, + 1, + 0, + 0, + 255 /* none */ }; + + + static uint8_t tab2[16] = { 255 /* none */, + 3, + 1, + 3, + 2, + 255 /* na */, + 2, + 3, + 3, + 2, + 255 /* na */, + 2, + 3, + 1, + 3, + 255 /* none */ }; + + // Create the coordinates of the rectangle + Vector x[4]; + AssignVector(x[0], xmin, ymin, 1.0); + AssignVector(x[1], xmax, ymin, 1.0); + AssignVector(x[2], xmax, ymax, 1.0); + AssignVector(x[3], xmin, ymax, 1.0); + + // Move to homogoneous coordinates in 2D + Vector p; + + { + Vector a, b; + AssignVector(a, ax, ay, 1.0); + AssignVector(b, bx, by, 1.0); + CrossProduct(p, a, b); + } + + uint8_t c = 0; + + for (unsigned int k = 0; k < 4; k++) + { + if (boost::numeric::ublas::inner_prod(p, x[k]) >= 0) + { + c |= (1 << k); + } + } + + assert(c < 16); + + uint8_t i = tab1[c]; + uint8_t j = tab2[c]; + + if (i == 255 || j == 255) + { + return false; // No intersection + } + else + { + Vector a, b, e; + CrossProduct(e, x[i], x[(i + 1) % 4]); + CrossProduct(a, p, e); + CrossProduct(e, x[j], x[(j + 1) % 4]); + CrossProduct(b, p, e); + + // Go back to non-homogeneous coordinates + x1 = a[0] / a[2]; + y1 = a[1] / a[2]; + x2 = b[0] / b[2]; + y2 = b[1] / b[2]; + + return true; + } + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/GeometryToolbox.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,111 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <boost/numeric/ublas/vector.hpp> + + +namespace OrthancStone +{ + typedef boost::numeric::ublas::vector<double> Vector; + + namespace GeometryToolbox + { + void Print(const Vector& v); + + bool ParseVector(Vector& target, + const std::string& value); + + void AssignVector(Vector& v, + double v1, + double v2); + + void AssignVector(Vector& v, + double v1, + double v2, + double v3); + + inline bool IsNear(double x, + double y, + double threshold) + { + return fabs(x - y) < threshold; + } + + bool IsNear(double x, + double y); + + inline bool IsCloseToZero(double x) + { + return IsNear(x, 0.0); + } + + void NormalizeVector(Vector& u); + + void CrossProduct(Vector& result, + const Vector& u, + const Vector& v); + + void ProjectPointOntoPlane(Vector& result, + const Vector& point, + const Vector& planeNormal, + const Vector& planeOrigin); + + bool IsParallel(const Vector& u, + const Vector& v); + + bool IsParallelOrOpposite(bool& isOpposite, + const Vector& u, + const Vector& v); + + bool IntersectTwoPlanes(Vector& p, + Vector& direction, + const Vector& origin1, + const Vector& normal1, + const Vector& origin2, + const Vector& normal2); + + bool ClipLineToRectangle(double& x1, // Coordinates of the clipped line (out) + double& y1, + double& x2, + double& y2, + const double ax, // Two points defining the line (in) + const double ay, + const double bx, + const double by, + const double& xmin, // Coordinates of the rectangle (in) + const double& ymin, + const double& xmax, + const double& ymax); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/ISeriesLoader.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,66 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ParallelSlices.h" + +#include "../Orthanc/Core/Images/ImageAccessor.h" + +namespace OrthancStone +{ + // This class is NOT thread-safe + class ISeriesLoader : public IThreadUnsafe + { + public: + virtual ParallelSlices& GetGeometry() = 0; + + virtual Orthanc::PixelFormat GetPixelFormat() = 0; + + virtual unsigned int GetWidth() = 0; + + virtual unsigned int GetHeight() = 0; + + virtual DicomDataset* DownloadDicom(size_t index) = 0; + + // This downloads the frame from Orthanc. The resulting pixel + // format must be Grayscale8, Grayscale16, SignedGrayscale16 or + // RGB24. Orthanc Stone assumes the conversion of the photometric + // interpretation is done by Orthanc. + virtual Orthanc::ImageAccessor* DownloadFrame(size_t index) = 0; + + virtual Orthanc::ImageAccessor* DownloadJpegFrame(size_t index, + unsigned int quality) = 0; + + virtual bool IsJpegAvailable() = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/IThreadSafety.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,68 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <boost/noncopyable.hpp> + +namespace OrthancStone +{ + /** + * Dummy interface to explicitely tag the interfaces whose derived + * class must be thread-safe. The different methods of such classes + * might be simlultaneously invoked by several threads, and should + * be properly protected by mutexes. + **/ + class IThreadSafe : public boost::noncopyable + { + public: + virtual ~IThreadSafe() + { + } + }; + + + /** + * Dummy interface to explicitely tag the interfaces that are NOT + * expected to be thread-safe. The Orthanc Stone framework ensures + * that at most one method of such classes will be invoked at a + * given time. Such classes are automatically protected by the + * Orthanc Stone framework wherever required. + **/ + class IThreadUnsafe : public boost::noncopyable + { + public: + virtual ~IThreadUnsafe() + { + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/ObserversRegistry.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,136 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Orthanc/Core/OrthancException.h" + +#include <boost/noncopyable.hpp> +#include <boost/thread/mutex.hpp> +#include <set> + +namespace OrthancStone +{ + template < + typename Source, + typename Observer = typename Source::IChangeObserver + > + class ObserversRegistry : public boost::noncopyable + { + private: + struct ChangeFunctor : public boost::noncopyable + { + void operator() (Observer& observer, + const Source& source) + { + observer.NotifyChange(source); + } + }; + + typedef std::set<Observer*> Observers; + + boost::mutex mutex_; + Observers observers_; + bool empty_; + + public: + ObserversRegistry() : empty_(true) + { + } + + template <typename Functor> + void Notify(const Source* source, + Functor& functor) + { + if (empty_) + { + // Optimization to avoid the unnecessary locking of the mutex + return; + } + + if (source == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + boost::mutex::scoped_lock lock(mutex_); + + for (typename Observers::const_iterator observer = observers_.begin(); + observer != observers_.end(); ++observer) + { + functor(**observer, *source); + } + } + + template <typename Functor> + void Notify(const Source* source) + { + // Use the default functor + Functor functor; + Notify(source, functor); + } + + void NotifyChange(const Source* source) + { + Notify<ChangeFunctor>(source); + } + + void Register(Observer& observer) + { + empty_ = false; + + boost::mutex::scoped_lock lock(mutex_); + observers_.insert(&observer); + } + + void Unregister(Observer& observer) + { + boost::mutex::scoped_lock lock(mutex_); + observers_.erase(&observer); + + if (observers_.empty()) + { + empty_ = true; + } + } + + bool IsEmpty() + { +#if 0 + boost::mutex::scoped_lock lock(mutex_); + return observers_.empty(); +#else + return empty_; +#endif + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/OrthancSeriesLoader.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,444 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "OrthancSeriesLoader.h" + +#include "../Messaging/MessagingToolbox.h" +#include "../Orthanc/Core/Images/Image.h" +#include "../Orthanc/Core/Images/ImageProcessing.h" +#include "../Orthanc/Core/Logging.h" +#include "../Orthanc/Core/OrthancException.h" +#include "DicomFrameConverter.h" + +namespace OrthancStone +{ + class OrthancSeriesLoader::Slice : public boost::noncopyable + { + private: + std::string instanceId_; + SliceGeometry geometry_; + double projectionAlongNormal_; + + public: + Slice(const std::string& instanceId, + const std::string& imagePositionPatient, + const std::string& imageOrientationPatient) : + instanceId_(instanceId), + geometry_(imagePositionPatient, imageOrientationPatient) + { + } + + const std::string GetInstanceId() const + { + return instanceId_; + } + + const SliceGeometry& GetGeometry() const + { + return geometry_; + } + + void SetNormal(const Vector& normal) + { + projectionAlongNormal_ = boost::numeric::ublas::inner_prod(geometry_.GetOrigin(), normal); + } + + double GetProjectionAlongNormal() const + { + return projectionAlongNormal_; + } + }; + + + class OrthancSeriesLoader::SetOfSlices : public boost::noncopyable + { + private: + std::vector<Slice*> slices_; + + struct Comparator + { + bool operator() (const Slice* const a, + const Slice* const b) const + { + return a->GetProjectionAlongNormal() < b->GetProjectionAlongNormal(); + } + }; + + public: + ~SetOfSlices() + { + for (size_t i = 0; i < slices_.size(); i++) + { + assert(slices_[i] != NULL); + delete slices_[i]; + } + } + + void Reserve(size_t size) + { + slices_.reserve(size); + } + + void AddSlice(const std::string& instanceId, + const std::string& imagePositionPatient, + const std::string& imageOrientationPatient) + { + slices_.push_back(new Slice(instanceId, imagePositionPatient, imageOrientationPatient)); + } + + size_t GetSliceCount() const + { + return slices_.size(); + } + + const Slice& GetSlice(size_t index) const + { + assert(slices_[index] != NULL); + return *slices_[index]; + } + + void Sort(const Vector& normal) + { + for (size_t i = 0; i < slices_.size(); i++) + { + slices_[i]->SetNormal(normal); + } + + Comparator comparator; + std::sort(slices_.begin(), slices_.end(), comparator); + } + + void LoadSeriesFast(IOrthancConnection& orthanc, + const std::string& series) + { + // Retrieve the orientation of this series + Json::Value info; + MessagingToolbox::RestApiGet(info, orthanc, "/series/" + series); + + if (info.type() != Json::objectValue || + !info.isMember("MainDicomTags") || + info["MainDicomTags"].type() != Json::objectValue || + !info["MainDicomTags"].isMember("ImageOrientationPatient") || + info["MainDicomTags"]["ImageOrientationPatient"].type() != Json::stringValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + std::string imageOrientationPatient = info["MainDicomTags"]["ImageOrientationPatient"].asString(); + + + // Retrieve the Orthanc ID of all the instances of this series + Json::Value instances; + MessagingToolbox::RestApiGet(instances, orthanc, "/series/" + series + "/instances"); + + if (instances.type() != Json::arrayValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + if (instances.size() == 0) + { + LOG(ERROR) << "This series is empty"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); + } + + + // Retrieve the DICOM tags of all the instances + std::vector<std::string> instancesId; + + instancesId.resize(instances.size()); + Reserve(instances.size()); + + for (Json::Value::ArrayIndex i = 0; i < instances.size(); i++) + { + if (instances[i].type() != Json::objectValue || + !instances[i].isMember("ID") || + !instances[i].isMember("MainDicomTags") || + instances[i]["ID"].type() != Json::stringValue || + instances[i]["MainDicomTags"].type() != Json::objectValue || + !instances[i]["MainDicomTags"].isMember("ImagePositionPatient") || + instances[i]["MainDicomTags"]["ImagePositionPatient"].type() != Json::stringValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + instancesId[i] = instances[i]["ID"].asString(); + AddSlice(instancesId[i], + instances[i]["MainDicomTags"]["ImagePositionPatient"].asString(), + imageOrientationPatient); + } + } + + assert(GetSliceCount() == instances.size()); + } + + + void LoadSeriesSafe(IOrthancConnection& orthanc, + const std::string& seriesId) + { + Json::Value series; + MessagingToolbox::RestApiGet(series, orthanc, "/series/" + seriesId + "/instances-tags?simplify"); + + if (series.type() != Json::objectValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + if (series.size() == 0) + { + LOG(ERROR) << "This series is empty"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); + } + + Json::Value::Members instances = series.getMemberNames(); + + Reserve(instances.size()); + + for (Json::Value::ArrayIndex i = 0; i < instances.size(); i++) + { + const Json::Value& tags = series[instances[i]]; + + if (tags.type() != Json::objectValue || + !tags.isMember("ImagePositionPatient") || + !tags.isMember("ImageOrientationPatient") || + tags["ImagePositionPatient"].type() != Json::stringValue || + tags["ImageOrientationPatient"].type() != Json::stringValue) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + else + { + AddSlice(instances[i], + tags["ImagePositionPatient"].asString(), + tags["ImageOrientationPatient"].asString()); + } + } + + assert(GetSliceCount() == instances.size()); + } + + + void SelectNormal(Vector& normal) const + { + std::vector<Vector> normalCandidates; + std::vector<unsigned int> normalCount; + + bool found = false; + + for (size_t i = 0; !found && i < GetSliceCount(); i++) + { + const Vector& normal = GetSlice(i).GetGeometry().GetNormal(); + + bool add = true; + for (size_t j = 0; add && j < normalCandidates.size(); j++) // (*) + { + if (GeometryToolbox::IsParallel(normal, normalCandidates[j])) + { + normalCount[j] += 1; + add = false; + } + } + + if (add) + { + if (normalCount.size() > 2) + { + // To get linear-time complexity in (*). This heuristics + // allows the series to have one single frame that is + // not parallel to the others (such a frame could be a + // generated preview) + found = false; + } + else + { + normalCandidates.push_back(normal); + normalCount.push_back(1); + } + } + } + + for (size_t i = 0; !found && i < normalCandidates.size(); i++) + { + unsigned int count = normalCount[i]; + if (count == GetSliceCount() || + count + 1 == GetSliceCount()) + { + normal = normalCandidates[i]; + found = true; + } + } + + if (!found) + { + LOG(ERROR) << "Cannot select a normal that is shared by most of the slices of this series"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + + void FilterNormal(const Vector& normal) + { + size_t pos = 0; + + for (size_t i = 0; i < slices_.size(); i++) + { + if (GeometryToolbox::IsParallel(normal, slices_[i]->GetGeometry().GetNormal())) + { + // This slice is compatible with the selected normal + slices_[pos] = slices_[i]; + pos += 1; + } + else + { + delete slices_[i]; + slices_[i] = NULL; + } + } + + slices_.resize(pos); + } + }; + + + + OrthancSeriesLoader::OrthancSeriesLoader(IOrthancConnection& orthanc, + const std::string& series) : + orthanc_(orthanc), + slices_(new SetOfSlices) + { + /** + * The function "LoadSeriesFast()" might now behave properly if + * some slice has some outsider value for its normal, which + * happens sometimes on reprojected series (e.g. coronal and + * sagittal of Delphine). Don't use it. + **/ + + slices_->LoadSeriesSafe(orthanc, series); + + Vector normal; + slices_->SelectNormal(normal); + slices_->FilterNormal(normal); + slices_->Sort(normal); + + if (slices_->GetSliceCount() == 0) // Sanity check + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + for (size_t i = 0; i < slices_->GetSliceCount(); i++) + { + assert(GeometryToolbox::IsParallel(normal, slices_->GetSlice(i).GetGeometry().GetNormal())); + geometry_.AddSlice(slices_->GetSlice(i).GetGeometry()); + } + + std::auto_ptr<DicomDataset> dataset(new DicomDataset(orthanc_, slices_->GetSlice(0).GetInstanceId())); + if (!dataset->HasTag(DICOM_TAG_ROWS) || + !dataset->HasTag(DICOM_TAG_COLUMNS)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InexistentTag); + } + + DicomFrameConverter converter; + converter.ReadParameters(*dataset); + + format_ = converter.GetExpectedPixelFormat(); + width_ = dataset->GetUnsignedIntegerValue(DICOM_TAG_COLUMNS); + height_ = dataset->GetUnsignedIntegerValue(DICOM_TAG_ROWS); + } + + + DicomDataset* OrthancSeriesLoader::DownloadDicom(size_t index) + { + std::auto_ptr<DicomDataset> dataset(new DicomDataset(orthanc_, slices_->GetSlice(index).GetInstanceId())); + + if (dataset->HasTag(DICOM_TAG_NUMBER_OF_FRAMES) && + dataset->GetUnsignedIntegerValue(DICOM_TAG_NUMBER_OF_FRAMES) != 1) + { + LOG(ERROR) << "One instance in this series has more than 1 frame"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + return dataset.release(); + } + + + void OrthancSeriesLoader::CheckFrame(const Orthanc::ImageAccessor& frame) const + { + if (frame.GetFormat() != format_ || + frame.GetWidth() != width_ || + frame.GetHeight() != height_) + { + LOG(ERROR) << "The parameters of this series vary accross its slices"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + } + + + Orthanc::ImageAccessor* OrthancSeriesLoader::DownloadFrame(size_t index) + { + const Slice& slice = slices_->GetSlice(index); + + std::auto_ptr<Orthanc::ImageAccessor> frame + (MessagingToolbox::DecodeFrame(orthanc_, slice.GetInstanceId(), 0, format_)); + + if (frame.get() != NULL) + { + CheckFrame(*frame); + } + + return frame.release(); + } + + + Orthanc::ImageAccessor* OrthancSeriesLoader::DownloadJpegFrame(size_t index, + unsigned int quality) + { + const Slice& slice = slices_->GetSlice(index); + + std::auto_ptr<Orthanc::ImageAccessor> frame + (MessagingToolbox::DecodeJpegFrame(orthanc_, slice.GetInstanceId(), 0, quality, format_)); + + if (frame.get() != NULL) + { + CheckFrame(*frame); + } + + return frame.release(); + } + + + bool OrthancSeriesLoader::IsJpegAvailable() + { + return MessagingToolbox::HasWebViewerInstalled(orthanc_); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/OrthancSeriesLoader.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,93 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ISeriesLoader.h" + +#include <boost/shared_ptr.hpp> + +namespace OrthancStone +{ + // This class is NOT thread-safe + // It sorts the slices from a given series, give access to their + // geometry and individual frames, making the assumption that there + // is a single frame in each instance of the series + class OrthancSeriesLoader : public ISeriesLoader + { + private: + class Slice; + class SetOfSlices; + + IOrthancConnection& orthanc_; + boost::shared_ptr<SetOfSlices> slices_; + ParallelSlices geometry_; + Orthanc::PixelFormat format_; + unsigned int width_; + unsigned int height_; + + void CheckFrame(const Orthanc::ImageAccessor& frame) const; + + public: + OrthancSeriesLoader(IOrthancConnection& orthanc, + const std::string& series); + + virtual Orthanc::PixelFormat GetPixelFormat() + { + return format_; + } + + virtual ParallelSlices& GetGeometry() + { + return geometry_; + } + + virtual unsigned int GetWidth() + { + return width_; + } + + virtual unsigned int GetHeight() + { + return height_; + } + + virtual DicomDataset* DownloadDicom(size_t index); + + virtual Orthanc::ImageAccessor* DownloadFrame(size_t index); + + virtual Orthanc::ImageAccessor* DownloadJpegFrame(size_t index, + unsigned int quality); + + virtual bool IsJpegAvailable(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/ParallelSlices.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,158 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "ParallelSlices.h" + +#include "../Orthanc/Core/Logging.h" +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + ParallelSlices::ParallelSlices() + { + GeometryToolbox::AssignVector(normal_, 0, 0, 1); + } + + + ParallelSlices::ParallelSlices(const ParallelSlices& other) + { + normal_ = other.normal_; + + slices_.resize(other.slices_.size()); + + for (size_t i = 0; i < slices_.size(); i++) + { + assert(other.slices_[i] != NULL); + slices_[i] = new SliceGeometry(*other.slices_[i]); + } + } + + + ParallelSlices::~ParallelSlices() + { + for (size_t i = 0; i < slices_.size(); i++) + { + if (slices_[i] != NULL) + { + delete slices_[i]; + slices_[i] = NULL; + } + } + } + + + void ParallelSlices::AddSlice(const SliceGeometry& slice) + { + if (slices_.empty()) + { + normal_ = slice.GetNormal(); + slices_.push_back(new SliceGeometry(slice)); + } + else if (GeometryToolbox::IsParallel(slice.GetNormal(), normal_)) + { + slices_.push_back(new SliceGeometry(slice)); + } + else + { + LOG(ERROR) << "Trying to add a slice that is not parallel to the previous ones"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + } + + + void ParallelSlices::AddSlice(const Vector& origin, + const Vector& axisX, + const Vector& axisY) + { + SliceGeometry slice(origin, axisX, axisY); + AddSlice(slice); + } + + + const SliceGeometry& ParallelSlices::GetSlice(size_t index) const + { + if (index >= slices_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + return *slices_[index]; + } + } + + + bool ParallelSlices::ComputeClosestSlice(size_t& closestSlice, + double& closestDistance, + const Vector& origin) const + { + if (slices_.empty()) + { + return false; + } + + double reference = boost::numeric::ublas::inner_prod(origin, normal_); + + closestSlice = 0; + closestDistance = std::numeric_limits<double>::infinity(); + + for (size_t i = 0; i < slices_.size(); i++) + { + double distance = fabs(boost::numeric::ublas::inner_prod(slices_[i]->GetOrigin(), normal_) - reference); + + if (distance < closestDistance) + { + closestSlice = i; + closestDistance = distance; + } + } + + return true; + } + + + ParallelSlices* ParallelSlices::Reverse() const + { + std::auto_ptr<ParallelSlices> reversed(new ParallelSlices); + + for (size_t i = slices_.size(); i > 0; i--) + { + const SliceGeometry& slice = *slices_[i - 1]; + + reversed->AddSlice(slice.GetOrigin(), + -slice.GetAxisX(), + slice.GetAxisY()); + } + + return reversed.release(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/ParallelSlices.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,79 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SliceGeometry.h" + +namespace OrthancStone +{ + // This class is NOT thread-safe + class ParallelSlices + { + private: + Vector normal_; + std::vector<SliceGeometry*> slices_; + + ParallelSlices& operator= (const ParallelSlices& other); // Forbidden + + public: + ParallelSlices(); + + ParallelSlices(const ParallelSlices& other); + + ~ParallelSlices(); + + const Vector& GetNormal() const + { + return normal_; + } + + void AddSlice(const SliceGeometry& slice); + + void AddSlice(const Vector& origin, + const Vector& axisX, + const Vector& axisY); + + size_t GetSliceCount() const + { + return slices_.size(); + } + + const SliceGeometry& GetSlice(size_t index) const; + + bool ComputeClosestSlice(size_t& closestSlice, + double& closestDistance, + const Vector& origin) const; + + ParallelSlices* Reverse() const; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/ParallelSlicesCursor.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,246 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "ParallelSlicesCursor.h" + +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + size_t ParallelSlicesCursor::GetDefaultSlice() + { + if (slices_.get() == NULL) + { + return 0; + } + else + { + return slices_->GetSliceCount() / 2; + } + } + + + size_t ParallelSlicesCursor::GetSliceCount() + { + boost::mutex::scoped_lock lock(mutex_); + + if (slices_.get() == NULL) + { + return 0; + } + else + { + return slices_->GetSliceCount(); + } + } + + + SliceGeometry ParallelSlicesCursor::GetSlice(size_t slice) + { + boost::mutex::scoped_lock lock(mutex_); + + if (slices_.get() == NULL) + { + return SliceGeometry(); + } + else + { + return slices_->GetSlice(slice); + } + } + + + void ParallelSlicesCursor::SetGeometry(const ParallelSlices& slices) + { + boost::mutex::scoped_lock lock(mutex_); + + slices_.reset(new ParallelSlices(slices)); + + currentSlice_ = GetDefaultSlice(); + } + + + SliceGeometry ParallelSlicesCursor::GetCurrentSlice() + { + boost::mutex::scoped_lock lock(mutex_); + + if (slices_.get() != NULL && + currentSlice_ < slices_->GetSliceCount()) + { + return slices_->GetSlice(currentSlice_); + } + else + { + return SliceGeometry(); // No slice is available, return the canonical geometry + } + } + + + bool ParallelSlicesCursor::SetDefaultSlice() + { + boost::mutex::scoped_lock lock(mutex_); + + size_t slice = GetDefaultSlice(); + + if (currentSlice_ != slice) + { + currentSlice_ = slice; + return true; + } + else + { + return false; + } + } + + + bool ParallelSlicesCursor::ApplyOffset(SliceOffsetMode mode, + int offset) + { + boost::mutex::scoped_lock lock(mutex_); + + if (slices_.get() == NULL) + { + return false; + } + + int count = slices_->GetSliceCount(); + if (count == 0) + { + return false; + } + + int slice; + if (static_cast<int>(currentSlice_) >= count) + { + slice = count - 1; + } + else + { + slice = currentSlice_; + } + + switch (mode) + { + case SliceOffsetMode_Absolute: + { + slice = offset; + break; + } + + case SliceOffsetMode_Relative: + { + slice += offset; + break; + } + + case SliceOffsetMode_Loop: + { + slice += offset; + while (slice < 0) + { + slice += count; + } + + while (slice >= count) + { + slice -= count; + } + + break; + } + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + if (slice < 0) + { + slice = 0; + } + + if (slice >= count) + { + slice = count - 1; + } + + if (slice != static_cast<int>(currentSlice_)) + { + currentSlice_ = static_cast<int>(slice); + return true; + } + else + { + return false; + } + } + + + bool ParallelSlicesCursor::ApplyWheelEvent(MouseWheelDirection direction, + KeyboardModifiers modifiers) + { + int offset = (modifiers & KeyboardModifiers_Control ? 10 : 1); + + switch (direction) + { + case MouseWheelDirection_Down: + return ApplyOffset(SliceOffsetMode_Relative, -offset); + + case MouseWheelDirection_Up: + return ApplyOffset(SliceOffsetMode_Relative, offset); + + default: + return false; + } + } + + + bool ParallelSlicesCursor::LookupSliceContainingPoint(const Vector& p) + { + boost::mutex::scoped_lock lock(mutex_); + + size_t slice; + double distance; + + if (slices_.get() != NULL && + slices_->ComputeClosestSlice(slice, distance, p)) + { + if (currentSlice_ != slice) + { + currentSlice_ = slice; + return true; + } + } + + return false; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/ParallelSlicesCursor.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,80 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ParallelSlices.h" +#include "../Enumerations.h" + +#include <boost/thread/mutex.hpp> + +namespace OrthancStone +{ + // This class is thread-safe + class ParallelSlicesCursor : public boost::noncopyable + { + private: + boost::mutex mutex_; + std::auto_ptr<ParallelSlices> slices_; + size_t currentSlice_; + + size_t GetDefaultSlice(); + + public: + ParallelSlicesCursor() : + currentSlice_(0) + { + } + + void SetGeometry(const ParallelSlices& slices); + + size_t GetSliceCount(); + + SliceGeometry GetSlice(size_t slice); + + SliceGeometry GetCurrentSlice(); + + // Returns "true" iff. the slice has actually changed + bool SetDefaultSlice(); + + // Returns "true" iff. the slice has actually changed + bool ApplyOffset(SliceOffsetMode mode, + int offset); + + // Returns "true" iff. the slice has actually changed + bool ApplyWheelEvent(MouseWheelDirection direction, + KeyboardModifiers modifiers); + + // Returns "true" iff. the slice has actually changed + bool LookupSliceContainingPoint(const Vector& p); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/SharedValue.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,69 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <boost/noncopyable.hpp> +#include <boost/thread/mutex.hpp> + +namespace OrthancStone +{ + // A value that is protected by a mutex, in order to be shared by + // multiple threads + template <typename T> + class SharedValue : public boost::noncopyable + { + private: + boost::mutex mutex_; + T value_; + + public: + class Locker : public boost::noncopyable + { + private: + boost::mutex::scoped_lock lock_; + T& value_; + + public: + Locker(SharedValue& shared) : + lock_(shared.mutex_), + value_(shared.value_) + { + } + + T& GetValue() const + { + return value_; + } + }; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/SliceGeometry.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,165 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "SliceGeometry.h" + +#include "GeometryToolbox.h" + +#include "../Orthanc/Core/Logging.h" +#include "../Orthanc/Core/Toolbox.h" +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + void SliceGeometry::CheckAndComputeNormal() + { + // DICOM expects normal vectors to define the axes: "The row and + // column direction cosine vectors shall be normal, i.e., the dot + // product of each direction cosine vector with itself shall be + // unity." + // http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.2.html + if (!GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(axisX_), 1.0) || + !GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(axisY_), 1.0)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + // The vectors within "Image Orientation Patient" must be + // orthogonal, according to the DICOM specification: "The row and + // column direction cosine vectors shall be orthogonal, i.e., + // their dot product shall be zero." + // http://dicom.nema.org/medical/dicom/current/output/chtml/part03/sect_C.7.6.2.html + if (!GeometryToolbox::IsCloseToZero(boost::numeric::ublas::inner_prod(axisX_, axisY_))) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + GeometryToolbox::CrossProduct(normal_, axisX_, axisY_); + + // Just a sanity check, it should be useless by construction + assert(GeometryToolbox::IsNear(boost::numeric::ublas::norm_2(normal_), 1.0)); + } + + + void SliceGeometry::SetupCanonical() + { + GeometryToolbox::AssignVector(origin_, 0, 0, 0); + GeometryToolbox::AssignVector(axisX_, 1, 0, 0); + GeometryToolbox::AssignVector(axisY_, 0, 1, 0); + CheckAndComputeNormal(); + } + + + SliceGeometry::SliceGeometry(const Vector& origin, + const Vector& axisX, + const Vector& axisY) : + origin_(origin), + axisX_(axisX), + axisY_(axisY) + { + CheckAndComputeNormal(); + } + + + void SliceGeometry::Setup(const std::string& imagePositionPatient, + const std::string& imageOrientationPatient) + { + std::string tmpPosition = Orthanc::Toolbox::StripSpaces(imagePositionPatient); + std::string tmpOrientation = Orthanc::Toolbox::StripSpaces(imageOrientationPatient); + + Vector orientation; + if (!GeometryToolbox::ParseVector(origin_, tmpPosition) || + !GeometryToolbox::ParseVector(orientation, tmpOrientation) || + origin_.size() != 3 || + orientation.size() != 6) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + axisX_.resize(3); + axisX_[0] = orientation[0]; + axisX_[1] = orientation[1]; + axisX_[2] = orientation[2]; + + axisY_.resize(3); + axisY_[0] = orientation[3]; + axisY_[1] = orientation[4]; + axisY_[2] = orientation[5]; + + CheckAndComputeNormal(); + } + + + SliceGeometry::SliceGeometry(const DicomDataset& dicom) + { + if (dicom.HasTag(DICOM_TAG_IMAGE_POSITION_PATIENT) && + dicom.HasTag(DICOM_TAG_IMAGE_ORIENTATION_PATIENT)) + { + Setup(dicom.GetStringValue(DICOM_TAG_IMAGE_POSITION_PATIENT), + dicom.GetStringValue(DICOM_TAG_IMAGE_ORIENTATION_PATIENT)); + } + else + { + SetupCanonical(); + } + } + + + Vector SliceGeometry::MapSliceToWorldCoordinates(double x, + double y) const + { + return origin_ + x * axisX_ + y * axisY_; + } + + + double SliceGeometry::ProjectAlongNormal(const Vector& point) const + { + return boost::numeric::ublas::inner_prod(point, normal_); + } + + + void SliceGeometry::ProjectPoint(double& offsetX, + double& offsetY, + const Vector& point) const + { + // Project the point onto the slice + Vector projection; + GeometryToolbox::ProjectPointOntoPlane(projection, point, normal_, origin_); + + // As the axes are orthonormal vectors thanks to + // CheckAndComputeNormal(), the following dot products give the + // offset of the origin of the slice wrt. the origin of the + // reference plane https://en.wikipedia.org/wiki/Vector_projection + offsetX = boost::numeric::ublas::inner_prod(axisX_, projection - origin_); + offsetY = boost::numeric::ublas::inner_prod(axisY_, projection - origin_); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/SliceGeometry.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,102 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "DicomDataset.h" + +namespace OrthancStone +{ + // Geometry of a 3D plane, NOT thread-safe + class SliceGeometry + { + private: + Vector origin_; + Vector normal_; + Vector axisX_; + Vector axisY_; + + void CheckAndComputeNormal(); + + void Setup(const std::string& imagePositionPatient, + const std::string& imageOrientationPatient); + + void SetupCanonical(); + + public: + SliceGeometry() + { + SetupCanonical(); + } + + SliceGeometry(const Vector& origin, + const Vector& axisX, + const Vector& axisY); + + SliceGeometry(const DicomDataset& dicom); + + SliceGeometry(const std::string& imagePositionPatient, + const std::string& imageOrientationPatient) + { + Setup(imagePositionPatient, imageOrientationPatient); + } + + const Vector& GetNormal() const + { + return normal_; + } + + const Vector& GetOrigin() const + { + return origin_; + } + + const Vector& GetAxisX() const + { + return axisX_; + } + + const Vector& GetAxisY() const + { + return axisY_; + } + + Vector MapSliceToWorldCoordinates(double x, + double y) const; + + double ProjectAlongNormal(const Vector& point) const; + + void ProjectPoint(double& offsetX, + double& offsetY, + const Vector& point) const; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/ViewportGeometry.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,217 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "ViewportGeometry.h" + +#include "../Orthanc/Core/Logging.h" +#include "../Orthanc/Core/OrthancException.h" + +#include <boost/math/special_functions/round.hpp> + +namespace OrthancStone +{ + void ViewportGeometry::ComputeTransform() + { + // The following lines must be read in reverse order! + cairo_matrix_t tmp; + + // Bring the center of the scene to the center of the view + cairo_matrix_init_translate(&transform_, + panX_ + static_cast<double>(width_) / 2.0, + panY_ + static_cast<double>(height_) / 2.0); + + // Apply the zoom around (0,0) + cairo_matrix_init_scale(&tmp, zoom_, zoom_); + cairo_matrix_multiply(&transform_, &tmp, &transform_); + + // Bring the center of the scene to (0,0) + cairo_matrix_init_translate(&tmp, -(x1_ + x2_) / 2.0, -(y1_ + y2_) / 2.0); + cairo_matrix_multiply(&transform_, &tmp, &transform_); + } + + + ViewportGeometry::ViewportGeometry() + { + x1_ = 0; + y1_ = 0; + x2_ = 0; + y2_ = 0; + + width_ = 0; + height_ = 0; + + zoom_ = 1; + panX_ = 0; + panY_ = 0; + + ComputeTransform(); + } + + + void ViewportGeometry::SetDisplaySize(unsigned int width, + unsigned int height) + { + if (width_ != width || + height_ != height) + { + LOG(INFO) << "New display size: " << width << "x" << height; + + width_ = width; + height_ = height; + + ComputeTransform(); + } + } + + + void ViewportGeometry::SetSceneExtent(double x1, + double y1, + double x2, + double y2) + { + if (x1 == x1_ && + y1 == y1_ && + x2 == x2_ && + y2 == y2_) + { + return; + } + else if (x1 > x2 || + y1 > y2) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + else + { + LOG(INFO) << "New scene extent: (" << x1 << "," << y1 << ") => (" << x2 << "," << y2 << ")"; + + x1_ = x1; + y1_ = y1; + x2_ = x2; + y2_ = y2; + + ComputeTransform(); + } + } + + + void ViewportGeometry::GetSceneExtent(double& x1, + double& y1, + double& x2, + double& y2) const + { + x1 = x1_; + y1 = y1_; + x2 = x2_; + y2 = y2_; + } + + + void ViewportGeometry::MapDisplayToScene(double& sceneX /* out */, + double& sceneY /* out */, + double x, + double y) const + { + cairo_matrix_t transform = transform_; + + if (cairo_matrix_invert(&transform) != CAIRO_STATUS_SUCCESS) + { + LOG(ERROR) << "Cannot invert singular matrix"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + sceneX = x; + sceneY = y; + cairo_matrix_transform_point(&transform, &sceneX, &sceneY); + } + + + void ViewportGeometry::MapSceneToDisplay(int& displayX /* out */, + int& displayY /* out */, + double x, + double y) const + { + cairo_matrix_transform_point(&transform_, &x, &y); + + displayX = static_cast<int>(boost::math::iround(x)); + displayY = static_cast<int>(boost::math::iround(y)); + } + + + void ViewportGeometry::SetDefaultView() + { + if (width_ > 0 && + height_ > 0 && + x2_ > x1_ + 10 * std::numeric_limits<double>::epsilon() && + y2_ > y1_ + 10 * std::numeric_limits<double>::epsilon()) + { + double zoomX = static_cast<double>(width_) / (x2_ - x1_); + double zoomY = static_cast<double>(height_) / (y2_ - y1_); + zoom_ = zoomX < zoomY ? zoomX : zoomY; + + panX_ = 0; + panY_ = 0; + + ComputeTransform(); + } + } + + + void ViewportGeometry::ApplyTransform(CairoContext& context) const + { + cairo_set_matrix(context.GetObject(), &transform_); + } + + + void ViewportGeometry::GetPan(double& x, + double& y) const + { + x = panX_; + y = panY_; + } + + + void ViewportGeometry::SetPan(double x, + double y) + { + panX_ = x; + panY_ = y; + ComputeTransform(); + } + + + void ViewportGeometry::SetZoom(double zoom) + { + zoom_ = zoom; + ComputeTransform(); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Toolbox/ViewportGeometry.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,115 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Viewport/CairoContext.h" + +namespace OrthancStone +{ + // Not thread-safe + class ViewportGeometry + { + private: + // Extent of the scene (in world units) + double x1_; + double y1_; + double x2_; + double y2_; + + // Size of the display (in pixels) + unsigned int width_; + unsigned int height_; + + // Zoom/pan + double zoom_; + double panX_; // In pixels (display units) + double panY_; + + cairo_matrix_t transform_; // Scene-to-display transformation + + void ComputeTransform(); + + public: + ViewportGeometry(); + + void SetDisplaySize(unsigned int width, + unsigned int height); + + void SetSceneExtent(double x1, + double y1, + double x2, + double y2); + + void GetSceneExtent(double& x1, + double& y1, + double& x2, + double& y2) const; + + void MapDisplayToScene(double& sceneX /* out */, + double& sceneY /* out */, + double x, + double y) const; + + void MapSceneToDisplay(int& displayX /* out */, + int& displayY /* out */, + double x, + double y) const; + + unsigned int GetDisplayWidth() const + { + return width_; + } + + unsigned int GetDisplayHeight() const + { + return height_; + } + + double GetZoom() const + { + return zoom_; + } + + void SetDefaultView(); + + void ApplyTransform(CairoContext& context) const; + + void GetPan(double& x, + double& y) const; + + void SetPan(double x, + double y); + + void SetZoom(double zoom); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Viewport/CairoContext.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,70 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "CairoContext.h" + +#include "../Orthanc/Core/Logging.h" +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + CairoContext::CairoContext(CairoSurface& surface) + { + context_ = cairo_create(surface.GetObject()); + if (!context_) + { + LOG(ERROR) << "Cannot create Cairo drawing context"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + CairoContext::~CairoContext() + { + if (context_ != NULL) + { + cairo_destroy(context_); + context_ = NULL; + } + } + + + void CairoContext::SetSourceColor(uint8_t red, + uint8_t green, + uint8_t blue) + { + cairo_set_source_rgb(context_, + static_cast<float>(red) / 255.0f, + static_cast<float>(green) / 255.0f, + static_cast<float>(blue) / 255.0f); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Viewport/CairoContext.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,64 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "CairoSurface.h" + +namespace OrthancStone +{ + // This is a RAII wrapper around the Cairo drawing context + class CairoContext : public boost::noncopyable + { + private: + cairo_t* context_; + + public: + CairoContext(CairoSurface& surface); + + ~CairoContext(); + + cairo_t* GetObject() + { + return context_; + } + + void SetSourceColor(uint8_t red, + uint8_t green, + uint8_t blue); + + void SetSourceColor(const uint8_t color[3]) + { + SetSourceColor(color[0], color[1], color[2]); + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Viewport/CairoFont.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,76 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "CairoFont.h" + +#include "../Orthanc/Core/Logging.h" +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + CairoFont::CairoFont(const char* family, + cairo_font_slant_t slant, + cairo_font_weight_t weight) + { + font_ = cairo_toy_font_face_create(family, slant, weight); + if (font_ == NULL) + { + LOG(ERROR) << "Unknown font: " << family; + throw Orthanc::OrthancException(Orthanc::ErrorCode_UnknownResource); + } + } + + + CairoFont::~CairoFont() + { + if (font_ != NULL) + { + cairo_font_face_destroy(font_); + } + } + + + void CairoFont::Draw(CairoContext& context, + const std::string& text, + double size) + { + if (size <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + cairo_t* cr = context.GetObject(); + cairo_set_font_face(cr, font_); + cairo_set_font_size(cr, size); + cairo_show_text(cr, text.c_str()); + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Viewport/CairoFont.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,55 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "CairoContext.h" + +namespace OrthancStone +{ + class CairoFont : public boost::noncopyable + { + private: + cairo_font_face_t* font_; + + public: + CairoFont(const char* family, + cairo_font_slant_t slant, + cairo_font_weight_t weight); + + ~CairoFont(); + + void Draw(CairoContext& context, + const std::string& text, + double size); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Viewport/CairoSurface.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,139 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "CairoSurface.h" + +#include "../Orthanc/Core/Logging.h" +#include "../Orthanc/Core/OrthancException.h" +#include "../Orthanc/Core/Images/ImageProcessing.h" + +namespace OrthancStone +{ + void CairoSurface::Release() + { + if (surface_) + { + cairo_surface_destroy(surface_); + surface_ = NULL; + } + } + + + void CairoSurface::Allocate(unsigned int width, + unsigned int height) + { + Release(); + + surface_ = cairo_image_surface_create(CAIRO_FORMAT_RGB24, width, height); + if (!surface_) + { + // Should never occur + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + if (cairo_surface_status(surface_) != CAIRO_STATUS_SUCCESS) + { + LOG(ERROR) << "Cannot create a Cairo surface"; + cairo_surface_destroy(surface_); + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + width_ = width; + height_ = height; + pitch_ = cairo_image_surface_get_stride(surface_); + buffer_ = cairo_image_surface_get_data(surface_); + } + + + CairoSurface::CairoSurface(Orthanc::ImageAccessor& accessor) + { + if (accessor.GetFormat() != Orthanc::PixelFormat_BGRA32) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_IncompatibleImageFormat); + } + + width_ = accessor.GetWidth(); + height_ = accessor.GetHeight(); + pitch_ = accessor.GetPitch(); + buffer_ = accessor.GetBuffer(); + + surface_ = cairo_image_surface_create_for_data + (reinterpret_cast<unsigned char*>(buffer_), CAIRO_FORMAT_RGB24, width_, height_, pitch_); + if (!surface_) + { + // Should never occur + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + if (cairo_surface_status(surface_) != CAIRO_STATUS_SUCCESS) + { + LOG(ERROR) << "Bad pitch for a Cairo surface"; + cairo_surface_destroy(surface_); + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + + void CairoSurface::SetSize(unsigned int width, + unsigned int height) + { + if (width_ != width || + height_ != height) + { + Allocate(width, height); + } + } + + + void CairoSurface::Copy(const CairoSurface& other) + { + Orthanc::ImageAccessor source = other.GetConstAccessor(); + Orthanc::ImageAccessor target = GetAccessor(); + Orthanc::ImageProcessing::Copy(target, source); + } + + + Orthanc::ImageAccessor CairoSurface::GetConstAccessor() const + { + Orthanc::ImageAccessor accessor; + accessor.AssignReadOnly(Orthanc::PixelFormat_BGRA32, width_, height_, pitch_, buffer_); + return accessor; + } + + + Orthanc::ImageAccessor CairoSurface::GetAccessor() + { + Orthanc::ImageAccessor accessor; + accessor.AssignWritable(Orthanc::PixelFormat_BGRA32, width_, height_, pitch_, buffer_); + return accessor; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Viewport/CairoSurface.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,116 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Orthanc/Core/Images/ImageAccessor.h" + +#include <boost/noncopyable.hpp> +#include <cairo.h> + +namespace OrthancStone +{ + class CairoSurface : public boost::noncopyable + { + private: + cairo_surface_t* surface_; + unsigned int width_; + unsigned int height_; + unsigned int pitch_; + void* buffer_; + + void Release(); + + void Allocate(unsigned int width, + unsigned int height); + + public: + CairoSurface() : + surface_(NULL) + { + Allocate(0, 0); + } + + CairoSurface(unsigned int width, + unsigned int height) : + surface_(NULL) + { + Allocate(width, height); + } + + CairoSurface(Orthanc::ImageAccessor& accessor); + + ~CairoSurface() + { + Release(); + } + + void SetSize(unsigned int width, + unsigned int height); + + void Copy(const CairoSurface& other); + + unsigned int GetWidth() const + { + return width_; + } + + unsigned int GetHeight() const + { + return height_; + } + + unsigned int GetPitch() const + { + return pitch_; + } + + const void* GetBuffer() const + { + return buffer_; + } + + void* GetBuffer() + { + return buffer_; + } + + cairo_surface_t* GetObject() + { + return surface_; + } + + Orthanc::ImageAccessor GetConstAccessor() const; + + Orthanc::ImageAccessor GetAccessor(); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Viewport/IMouseTracker.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,52 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "CairoSurface.h" +#include "../Toolbox/IThreadSafety.h" + +namespace OrthancStone +{ + // Not thread-safe + class IMouseTracker : public IThreadUnsafe + { + public: + virtual void Render(Orthanc::ImageAccessor& surface) = 0; + + virtual void MouseUp() = 0; + + // Returns "true" iff. the background scene must be repainted + virtual void MouseMove(int x, + int y) = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Viewport/IStatusBar.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,48 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include <string> +#include "../Toolbox/IThreadSafety.h" + +namespace OrthancStone +{ + // This class must be thread-safe + class IStatusBar : public IThreadSafe + { + public: + virtual void ClearMessage() = 0; + + virtual void SetMessage(const std::string& message) = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Viewport/IViewport.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,97 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Toolbox/IThreadSafety.h" +#include "IStatusBar.h" +#include "../Enumerations.h" + +#include "../Orthanc/Core/Images/ImageAccessor.h" + +namespace OrthancStone +{ + // This class must be thread-safe + class IViewport : public IThreadSafe + { + public: + class IChangeObserver : public boost::noncopyable + { + public: + virtual ~IChangeObserver() + { + } + + virtual void NotifyChange(const IViewport& scene) = 0; + }; + + virtual void Register(IChangeObserver& observer) = 0; + + virtual void Unregister(IChangeObserver& observer) = 0; + + virtual void SetStatusBar(IStatusBar& statusBar) = 0; + + virtual void ResetStatusBar() = 0; + + virtual void Start() = 0; + + virtual void Stop() = 0; + + virtual void SetSize(unsigned int width, + unsigned int height) = 0; + + // The function returns "true" iff. a new frame was rendered + virtual bool Render(Orthanc::ImageAccessor& surface) = 0; + + virtual void MouseDown(MouseButton button, + int x, + int y, + KeyboardModifiers modifiers) = 0; + + virtual void MouseUp() = 0; + + virtual void MouseMove(int x, + int y) = 0; + + virtual void MouseEnter() = 0; + + virtual void MouseLeave() = 0; + + virtual void MouseWheel(MouseWheelDirection direction, + int x, + int y, + KeyboardModifiers modifiers) = 0; + + virtual void KeyPressed(char key, + KeyboardModifiers modifiers) = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Viewport/WidgetViewport.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,321 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "WidgetViewport.h" + +#include "../Orthanc/Core/Images/ImageProcessing.h" +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + void WidgetViewport::UnregisterCentralWidget() + { + mouseTracker_.reset(NULL); + + if (centralWidget_.get() != NULL) + { + centralWidget_->Unregister(*this); + } + } + + + WidgetViewport::WidgetViewport() : + statusBar_(NULL), + isMouseOver_(false), + lastMouseX_(0), + lastMouseY_(0), + backgroundChanged_(false), + started_(false) + { + } + + + void WidgetViewport::SetStatusBar(IStatusBar& statusBar) + { + boost::mutex::scoped_lock lock(mutex_); + + statusBar_ = &statusBar; + + if (centralWidget_.get() != NULL) + { + centralWidget_->SetStatusBar(statusBar); + } + } + + + void WidgetViewport::ResetStatusBar() + { + boost::mutex::scoped_lock lock(mutex_); + + statusBar_ = NULL; + + if (centralWidget_.get() != NULL) + { + centralWidget_->ResetStatusBar(); + } + } + + + IWidget& WidgetViewport::SetCentralWidget(IWidget* widget) + { + if (started_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + boost::mutex::scoped_lock lock(mutex_); + + if (widget == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + UnregisterCentralWidget(); + + centralWidget_.reset(widget); + centralWidget_->Register(*this); + + if (statusBar_ == NULL) + { + centralWidget_->ResetStatusBar(); + } + else + { + centralWidget_->SetStatusBar(*statusBar_); + } + + backgroundChanged_ = true; + + return *widget; + } + + + void WidgetViewport::NotifyChange(const IWidget& widget) + { + backgroundChanged_ = true; + observers_.NotifyChange(this); + } + + + void WidgetViewport::Start() + { + boost::mutex::scoped_lock lock(mutex_); + + if (centralWidget_.get() != NULL) + { + centralWidget_->Start(); + } + + started_ = true; + } + + + void WidgetViewport::Stop() + { + boost::mutex::scoped_lock lock(mutex_); + + started_ = false; + + if (centralWidget_.get() != NULL) + { + centralWidget_->Stop(); + } + } + + + void WidgetViewport::SetSize(unsigned int width, + unsigned int height) + { + boost::mutex::scoped_lock lock(mutex_); + + background_.SetSize(width, height); + + if (centralWidget_.get() != NULL) + { + centralWidget_->SetSize(width, height); + } + + observers_.NotifyChange(this); + } + + + bool WidgetViewport::Render(Orthanc::ImageAccessor& surface) + { + boost::mutex::scoped_lock lock(mutex_); + + if (!started_ || + centralWidget_.get() == NULL) + { + return false; + } + + if (backgroundChanged_) + { + Orthanc::ImageAccessor accessor = background_.GetAccessor(); + if (!centralWidget_->Render(accessor)) + { + return false; + } + } + + Orthanc::ImageProcessing::Copy(surface, background_.GetAccessor()); + + if (mouseTracker_.get() != NULL) + { + mouseTracker_->Render(surface); + } + else if (isMouseOver_) + { + centralWidget_->RenderMouseOver(surface, lastMouseX_, lastMouseY_); + } + + return true; + } + + + void WidgetViewport::MouseDown(MouseButton button, + int x, + int y, + KeyboardModifiers modifiers) + { + boost::mutex::scoped_lock lock(mutex_); + + if (!started_) + { + return; + } + + lastMouseX_ = x; + lastMouseY_ = y; + + if (centralWidget_.get() != NULL) + { + mouseTracker_.reset(centralWidget_->CreateMouseTracker(button, x, y, modifiers)); + } + else + { + mouseTracker_.reset(NULL); + } + + observers_.NotifyChange(this);; + } + + + void WidgetViewport::MouseUp() + { + boost::mutex::scoped_lock lock(mutex_); + + if (!started_) + { + return; + } + + if (mouseTracker_.get() != NULL) + { + mouseTracker_->MouseUp(); + mouseTracker_.reset(NULL); + observers_.NotifyChange(this);; + } + } + + + void WidgetViewport::MouseMove(int x, + int y) + { + boost::mutex::scoped_lock lock(mutex_); + + if (!started_) + { + return; + } + + lastMouseX_ = x; + lastMouseY_ = y; + + if (mouseTracker_.get() != NULL) + { + mouseTracker_->MouseMove(x, y); + } + + // The scene must be repainted + observers_.NotifyChange(this); + } + + + void WidgetViewport::MouseEnter() + { + boost::mutex::scoped_lock lock(mutex_); + isMouseOver_ = true; + observers_.NotifyChange(this); + } + + + void WidgetViewport::MouseLeave() + { + boost::mutex::scoped_lock lock(mutex_); + isMouseOver_ = false; + observers_.NotifyChange(this); + } + + + void WidgetViewport::MouseWheel(MouseWheelDirection direction, + int x, + int y, + KeyboardModifiers modifiers) + { + boost::mutex::scoped_lock lock(mutex_); + + if (!started_) + { + return; + } + + if (centralWidget_.get() != NULL && + mouseTracker_.get() == NULL) + { + centralWidget_->MouseWheel(direction, x, y, modifiers); + } + } + + + void WidgetViewport::KeyPressed(char key, + KeyboardModifiers modifiers) + { + boost::mutex::scoped_lock lock(mutex_); + + if (centralWidget_.get() != NULL && + mouseTracker_.get() == NULL) + { + centralWidget_->KeyPressed(key, modifiers); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Viewport/WidgetViewport.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,117 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "IViewport.h" +#include "../Toolbox/ObserversRegistry.h" +#include "../Widgets/IWidget.h" + +namespace OrthancStone +{ + class WidgetViewport : + public IViewport, + public IWidget::IChangeObserver + { + private: + boost::mutex mutex_; + std::auto_ptr<IWidget> centralWidget_; + IStatusBar* statusBar_; + ObserversRegistry<IViewport> observers_; + std::auto_ptr<IMouseTracker> mouseTracker_; + bool isMouseOver_; + int lastMouseX_; + int lastMouseY_; + CairoSurface background_; + bool backgroundChanged_; + bool started_; + + void UnregisterCentralWidget(); + + public: + WidgetViewport(); + + virtual ~WidgetViewport() + { + UnregisterCentralWidget(); + } + + virtual void SetStatusBar(IStatusBar& statusBar); + + virtual void ResetStatusBar(); + + IWidget& SetCentralWidget(IWidget* widget); // Takes ownership + + virtual void NotifyChange(const IWidget& widget); + + virtual void Register(IViewport::IChangeObserver& observer) + { + observers_.Register(observer); + } + + virtual void Unregister(IViewport::IChangeObserver& observer) + { + observers_.Unregister(observer); + } + + virtual void Start(); + + virtual void Stop(); + + virtual void SetSize(unsigned int width, + unsigned int height); + + virtual bool Render(Orthanc::ImageAccessor& surface); + + virtual void MouseDown(MouseButton button, + int x, + int y, + KeyboardModifiers modifiers); + + virtual void MouseUp(); + + virtual void MouseMove(int x, + int y); + + virtual void MouseEnter(); + + virtual void MouseLeave(); + + virtual void MouseWheel(MouseWheelDirection direction, + int x, + int y, + KeyboardModifiers modifiers); + + virtual void KeyPressed(char key, + KeyboardModifiers modifiers); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/ISliceableVolume.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,61 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Toolbox/IThreadSafety.h" + +namespace OrthancStone +{ + class ISliceableVolume : public IThreadSafe + { + public: + // Must be thread-safe + class IChangeObserver : public boost::noncopyable + { + public: + virtual ~IChangeObserver() + { + } + + virtual void NotifyChange(const ISliceableVolume& volume) = 0; + }; + + virtual void Register(IChangeObserver& observer) = 0; + + virtual void Unregister(IChangeObserver& observer) = 0; + + virtual void Start() = 0; + + virtual void Stop() = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/ImageBuffer3D.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,332 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "ImageBuffer3D.h" + +#include "../Orthanc/Core/Images/ImageProcessing.h" +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + Orthanc::ImageAccessor ImageBuffer3D::GetAxialSliceAccessor(unsigned int slice, + bool readOnly) + { + if (slice >= depth_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + Orthanc::ImageAccessor accessor; + + if (readOnly) + { + accessor.AssignReadOnly(format_, width_, height_, image_.GetPitch(), + image_.GetConstRow(height_ * (depth_ - 1 - slice))); + } + else + { + accessor.AssignWritable(format_, width_, height_, image_.GetPitch(), + image_.GetRow(height_ * (depth_ - 1 - slice))); + } + + return accessor; + } + + + Orthanc::ImageAccessor ImageBuffer3D::GetCoronalSliceAccessor(unsigned int slice, + bool readOnly) + { + if (slice >= height_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + Orthanc::ImageAccessor accessor; + + if (readOnly) + { + accessor.AssignReadOnly(format_, width_, depth_, image_.GetPitch() * height_, + image_.GetConstRow(slice)); + } + else + { + accessor.AssignWritable(format_, width_, depth_, image_.GetPitch() * height_, + image_.GetRow(slice)); + } + + return accessor; + } + + + Orthanc::Image* ImageBuffer3D::ExtractSagittalSlice(unsigned int slice) const + { + if (slice >= width_) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::auto_ptr<Orthanc::Image> result(new Orthanc::Image(format_, height_, depth_)); + + unsigned int bytesPerPixel = Orthanc::GetBytesPerPixel(format_); + + for (unsigned int z = 0; z < depth_; z++) + { + //uint8_t* target = reinterpret_cast<uint8_t*>(result->GetRow(depth_ - 1 - z)); + uint8_t* target = reinterpret_cast<uint8_t*>(result->GetRow(z)); + + for (unsigned int y = 0; y < height_; y++) + { + const void* source = (reinterpret_cast<const uint8_t*>(image_.GetConstRow(y + z * height_)) + + bytesPerPixel * slice); + + memcpy(target, source, bytesPerPixel); + target += bytesPerPixel; + } + } + + return result.release(); + } + + + ImageBuffer3D::ImageBuffer3D(Orthanc::PixelFormat format, + unsigned int width, + unsigned int height, + unsigned int depth) : + image_(format, width, height * depth), + format_(format), + width_(width), + height_(height), + depth_(depth) + { + GeometryToolbox::AssignVector(voxelDimensions_, 1, 1, 1); + } + + + void ImageBuffer3D::Clear() + { + WriteLock lock(mutex_); + Orthanc::ImageProcessing::Set(image_, 0); + } + + + void ImageBuffer3D::SetAxialGeometry(const SliceGeometry& geometry) + { + WriteLock lock(mutex_); + axialGeometry_ = geometry; + } + + + void ImageBuffer3D::SetVoxelDimensions(double x, + double y, + double z) + { + if (x <= 0 || + y <= 0 || + z <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + { + WriteLock lock(mutex_); + GeometryToolbox::AssignVector(voxelDimensions_, x, y, z); + } + } + + + Vector ImageBuffer3D::GetVoxelDimensions(VolumeProjection projection) + { + ReadLock lock(mutex_); + + Vector result; + switch (projection) + { + case VolumeProjection_Axial: + result = voxelDimensions_; + break; + + case VolumeProjection_Coronal: + GeometryToolbox::AssignVector(result, voxelDimensions_[0], voxelDimensions_[2], voxelDimensions_[1]); + break; + + case VolumeProjection_Sagittal: + GeometryToolbox::AssignVector(result, voxelDimensions_[1], voxelDimensions_[2], voxelDimensions_[0]); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + return result; + } + + + void ImageBuffer3D::GetSliceSize(unsigned int& width, + unsigned int& height, + VolumeProjection projection) + { + switch (projection) + { + case VolumeProjection_Axial: + width = width_; + height = height_; + break; + + case VolumeProjection_Coronal: + width = width_; + height = depth_; + break; + + case VolumeProjection_Sagittal: + width = height_; + height = depth_; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + ParallelSlices* ImageBuffer3D::GetGeometry(VolumeProjection projection) + { + std::auto_ptr<ParallelSlices> result(new ParallelSlices); + + switch (projection) + { + case VolumeProjection_Axial: + for (unsigned int z = 0; z < depth_; z++) + { + Vector origin = axialGeometry_.GetOrigin(); + origin += static_cast<double>(z) * voxelDimensions_[2] * axialGeometry_.GetNormal(); + + result->AddSlice(origin, + axialGeometry_.GetAxisX(), + axialGeometry_.GetAxisY()); + } + break; + + case VolumeProjection_Coronal: + for (unsigned int y = 0; y < height_; y++) + { + Vector origin = axialGeometry_.GetOrigin(); + origin += static_cast<double>(y) * voxelDimensions_[1] * axialGeometry_.GetAxisY(); + origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal(); + + result->AddSlice(origin, + axialGeometry_.GetAxisX(), + -axialGeometry_.GetNormal()); + } + break; + + case VolumeProjection_Sagittal: + for (unsigned int x = 0; x < width_; x++) + { + Vector origin = axialGeometry_.GetOrigin(); + origin += static_cast<double>(x) * voxelDimensions_[0] * axialGeometry_.GetAxisX(); + origin += static_cast<double>(depth_ - 1) * voxelDimensions_[2] * axialGeometry_.GetNormal(); + + result->AddSlice(origin, + axialGeometry_.GetAxisY(), + -axialGeometry_.GetNormal()); + } + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + return result.release(); + } + + + ImageBuffer3D::SliceReader::SliceReader(ImageBuffer3D& that, + VolumeProjection projection, + unsigned int slice) : + lock_(that.mutex_) + { + switch (projection) + { + case VolumeProjection_Axial: + accessor_ = that.GetAxialSliceAccessor(slice, true); + break; + + case VolumeProjection_Coronal: + accessor_ = that.GetCoronalSliceAccessor(slice, true); + break; + + case VolumeProjection_Sagittal: + sagittal_.reset(that.ExtractSagittalSlice(slice)); + accessor_ = *sagittal_; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + void ImageBuffer3D::SliceWriter::Flush() + { + if (sagittal_.get() != NULL) + { + // TODO + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + } + + + ImageBuffer3D::SliceWriter::SliceWriter(ImageBuffer3D& that, + VolumeProjection projection, + unsigned int slice) : + lock_(that.mutex_) + { + switch (projection) + { + case VolumeProjection_Axial: + accessor_ = that.GetAxialSliceAccessor(slice, false); + break; + + case VolumeProjection_Coronal: + accessor_ = that.GetCoronalSliceAccessor(slice, false); + break; + + case VolumeProjection_Sagittal: + sagittal_.reset(that.ExtractSagittalSlice(slice)); + accessor_ = *sagittal_; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/ImageBuffer3D.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,163 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Enumerations.h" +#include "../Toolbox/IThreadSafety.h" +#include "../Toolbox/SliceGeometry.h" +#include "../Toolbox/ParallelSlices.h" + +#include "../Orthanc/Core/Images/Image.h" + +#include <boost/thread/shared_mutex.hpp> + +#if defined(_WIN32) +# include <boost/thread/win32/mutex.hpp> +#endif + +namespace OrthancStone +{ + class ImageBuffer3D : public IThreadSafe + { + private: + typedef boost::shared_mutex Mutex; + typedef boost::unique_lock<Mutex> WriteLock; + typedef boost::shared_lock<Mutex> ReadLock; + + Mutex mutex_; + SliceGeometry axialGeometry_; + Vector voxelDimensions_; + Orthanc::Image image_; + Orthanc::PixelFormat format_; + unsigned int width_; + unsigned int height_; + unsigned int depth_; + + Orthanc::ImageAccessor GetAxialSliceAccessor(unsigned int slice, + bool readOnly); + + Orthanc::ImageAccessor GetCoronalSliceAccessor(unsigned int slice, + bool readOnly); + + Orthanc::Image* ExtractSagittalSlice(unsigned int slice) const; + + public: + ImageBuffer3D(Orthanc::PixelFormat format, + unsigned int width, + unsigned int height, + unsigned int depth); + + void Clear(); + + // Set the geometry of the first axial slice (i.e. the one whose + // depth == 0) + void SetAxialGeometry(const SliceGeometry& geometry); + + void SetVoxelDimensions(double x, + double y, + double z); + + Vector GetVoxelDimensions(VolumeProjection projection); + + void GetSliceSize(unsigned int& width, + unsigned int& height, + VolumeProjection projection); + + unsigned int GetWidth() const + { + return width_; + } + + unsigned int GetHeight() const + { + return height_; + } + + unsigned int GetDepth() const + { + return depth_; + } + + Orthanc::PixelFormat GetFormat() const + { + return format_; + } + + ParallelSlices* GetGeometry(VolumeProjection projection); + + + class SliceReader : public boost::noncopyable + { + private: + ReadLock lock_; + Orthanc::ImageAccessor accessor_; + std::auto_ptr<Orthanc::Image> sagittal_; // Unused for axial and coronal + + public: + SliceReader(ImageBuffer3D& that, + VolumeProjection projection, + unsigned int slice); + + const Orthanc::ImageAccessor& GetAccessor() const + { + return accessor_; + } + }; + + + class SliceWriter : public boost::noncopyable + { + private: + WriteLock lock_; + Orthanc::ImageAccessor accessor_; + std::auto_ptr<Orthanc::Image> sagittal_; // Unused for axial and coronal + + void Flush(); + + public: + SliceWriter(ImageBuffer3D& that, + VolumeProjection projection, + unsigned int slice); + + ~SliceWriter() + { + Flush(); + } + + Orthanc::ImageAccessor& GetAccessor() + { + return accessor_; + } + }; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/VolumeImage.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,412 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "VolumeImage.h" + +#include "../Orthanc/Core/Logging.h" +#include "../Layers/FrameRenderer.h" + +namespace OrthancStone +{ + void VolumeImage::StoreUpdateTime() + { + lastUpdate_ = MessagingToolbox::Timestamp(); + } + + + void VolumeImage::NotifyChange(bool force) + { + bool go = false; + + if (force) + { + go = true; + } + else + { + // Don't notify the observers more than 5 times per second + MessagingToolbox::Timestamp now; + go = (now.GetMillisecondsSince(lastUpdate_) > 200); + } + + if (go) + { + StoreUpdateTime(); + observers_.NotifyChange(this); + } + } + + + void VolumeImage::LoadThread(VolumeImage* that) + { + while (that->continue_) + { + bool complete = false; + bool done = that->policy_->DownloadStep(complete); + + if (complete) + { + that->loadingComplete_ = true; + } + + if (done) + { + break; + } + else + { + that->NotifyChange(false); + } + } + + that->NotifyChange(true); + } + + + VolumeImage::VolumeImage(ISeriesLoader* loader) : // Takes ownership + loader_(loader), + threads_(1), + started_(false), + continue_(false), + loadingComplete_(false) + { + if (loader == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + const size_t depth = loader_->GetGeometry().GetSliceCount(); + + if (depth < 2) + { + // Empty or flat series + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat); + } + + // TODO Check pixel spacing, slice thickness, and windowing are + // constant across slices + referenceDataset_.reset(loader->DownloadDicom(0)); + + double spacingZ; + + { + // Project the origin of the first and last slices onto the normal + const SliceGeometry& s1 = loader_->GetGeometry().GetSlice(0); + const SliceGeometry& s2 = loader_->GetGeometry().GetSlice(depth - 1); + const Vector& normal = loader_->GetGeometry().GetNormal(); + + double p1 = boost::numeric::ublas::inner_prod(s1.GetOrigin(), normal); + double p2 = boost::numeric::ublas::inner_prod(s2.GetOrigin(), normal); + + spacingZ = fabs(p2 - p1) / static_cast<double>(depth); + + // TODO Check that all slices are evenly distributed + } + + buffer_.reset(new ImageBuffer3D(loader_->GetPixelFormat(), + loader_->GetWidth(), + loader_->GetHeight(), + depth)); + buffer_->Clear(); + buffer_->SetAxialGeometry(loader_->GetGeometry().GetSlice(0)); + + double spacingX, spacingY; + referenceDataset_->GetPixelSpacing(spacingX, spacingY); + buffer_->SetVoxelDimensions(spacingX, spacingY, spacingZ); + + // These 3 values are only used to speed up the LayerFactory + axialGeometry_.reset(buffer_->GetGeometry(VolumeProjection_Axial)); + coronalGeometry_.reset(buffer_->GetGeometry(VolumeProjection_Coronal)); + sagittalGeometry_.reset(buffer_->GetGeometry(VolumeProjection_Sagittal)); + } + + + VolumeImage::~VolumeImage() + { + Stop(); + + for (size_t i = 0; i < threads_.size(); i++) + { + if (threads_[i] != NULL) + { + delete threads_[i]; + } + } + } + + + void VolumeImage::SetDownloadPolicy(IDownloadPolicy* policy) // Takes ownership + { + if (started_) + { + LOG(ERROR) << "Cannot change the number of threads after a call to Start()"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + policy_.reset(policy); + } + + + void VolumeImage::SetThreadCount(size_t count) + { + if (started_) + { + LOG(ERROR) << "Cannot change the number of threads after a call to Start()"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + if (count <= 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + threads_.resize(count); + } + + + void VolumeImage::Register(IChangeObserver& observer) + { + observers_.Register(observer); + } + + + void VolumeImage::Unregister(IChangeObserver& observer) + { + observers_.Unregister(observer); + } + + + void VolumeImage::Start() + { + if (started_) + { + LOG(ERROR) << "Cannot call Start() twice"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + started_ = true; + StoreUpdateTime(); + + if (policy_.get() != NULL && + threads_.size() > 0) + { + continue_ = true; + policy_->Initialize(*buffer_, *loader_); + + for (size_t i = 0; i < threads_.size(); i++) + { + assert(threads_[i] == NULL); + threads_[i] = new boost::thread(LoadThread, this); + } + } + } + + + void VolumeImage::Stop() + { + if (!started_) + { + LOG(ERROR) << "Cannot call Stop() without calling Start() beforehand"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + if (continue_) + { + continue_ = false; + + for (size_t i = 0; i < threads_.size(); i++) + { + if (threads_[i]->joinable()) + { + threads_[i]->join(); + } + } + + assert(policy_.get() != NULL); + policy_->Finalize(); + } + } + + + ParallelSlices* VolumeImage::GetGeometry(VolumeProjection projection, + bool reverse) + { + std::auto_ptr<ParallelSlices> slices(buffer_->GetGeometry(projection)); + + if (reverse) + { + return slices->Reverse(); + } + else + { + return slices.release(); + } + } + + + bool VolumeImage::DetectProjection(VolumeProjection& projection, + bool& reverse, + const SliceGeometry& viewportSlice) + { + if (GeometryToolbox::IsParallelOrOpposite(reverse, viewportSlice.GetNormal(), axialGeometry_->GetNormal())) + { + projection = VolumeProjection_Axial; + return true; + } + else if (GeometryToolbox::IsParallelOrOpposite(reverse, viewportSlice.GetNormal(), sagittalGeometry_->GetNormal())) + { + projection = VolumeProjection_Sagittal; + return true; + } + else if (GeometryToolbox::IsParallelOrOpposite(reverse, viewportSlice.GetNormal(), coronalGeometry_->GetNormal())) + { + projection = VolumeProjection_Coronal; + return true; + } + else + { + return false; + } + } + + + const ParallelSlices& VolumeImage::GetGeometryInternal(VolumeProjection projection) + { + switch (projection) + { + case VolumeProjection_Axial: + return *axialGeometry_; + + case VolumeProjection_Sagittal: + return *sagittalGeometry_; + + case VolumeProjection_Coronal: + return *coronalGeometry_; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + + bool VolumeImage::LayerFactory::GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice) + { + VolumeProjection projection; + bool reverse; + + if (that_.buffer_->GetWidth() == 0 || + that_.buffer_->GetHeight() == 0 || + that_.buffer_->GetDepth() == 0 || + !that_.DetectProjection(projection, reverse, viewportSlice)) + { + return false; + } + else + { + Vector spacing = that_.GetVoxelDimensions(projection); + + unsigned int width, height; + that_.buffer_->GetSliceSize(width, height, projection); + + // As the slices of the volumic image are arranged in a box, + // we only consider one single reference slice (the one with index 0). + const SliceGeometry& volumeSlice = that_.GetGeometryInternal(projection).GetSlice(0); + + return FrameRenderer::ComputeFrameExtent(x1, y1, x2, y2, + viewportSlice, volumeSlice, + width, height, + spacing[0], spacing[1]); + } + } + + + ILayerRenderer* VolumeImage::LayerFactory::CreateLayerRenderer(const SliceGeometry& viewportSlice) + { + VolumeProjection projection; + bool reverse; + + if (that_.buffer_->GetWidth() == 0 || + that_.buffer_->GetHeight() == 0 || + that_.buffer_->GetDepth() == 0 || + !that_.DetectProjection(projection, reverse, viewportSlice)) + { + return NULL; + } + + const ParallelSlices& geometry = that_.GetGeometryInternal(projection); + + size_t closest; + double distance; + + const Vector spacing = that_.GetVoxelDimensions(projection); + const double sliceThickness = spacing[2]; + + if (geometry.ComputeClosestSlice(closest, distance, viewportSlice.GetOrigin()) && + distance <= sliceThickness / 2.0) + { + bool isFullQuality; + + if (projection == VolumeProjection_Axial && + that_.policy_.get() != NULL) + { + isFullQuality = that_.policy_->IsFullQualityAxial(closest); + } + else + { + isFullQuality = that_.IsLoadingComplete(); + } + + std::auto_ptr<Orthanc::Image> frame; + SliceGeometry frameSlice = geometry.GetSlice(closest); + + { + ImageBuffer3D::SliceReader reader(*that_.buffer_, projection, closest); + frame.reset(Orthanc::Image::Clone(reader.GetAccessor())); + } + + return FrameRenderer::CreateRenderer(frame.release(), + viewportSlice, + frameSlice, + *that_.referenceDataset_, + spacing[0], spacing[1], + isFullQuality); + } + else + { + return NULL; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/VolumeImage.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,159 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "ISliceableVolume.h" +#include "ImageBuffer3D.h" +#include "../Toolbox/ISeriesLoader.h" +#include "../Toolbox/ObserversRegistry.h" +#include "../Messaging/MessagingToolbox.h" +#include "../Layers/ILayerRendererFactory.h" + +#include <boost/thread.hpp> + +namespace OrthancStone +{ + class VolumeImage : public ISliceableVolume + { + public: + class IDownloadPolicy : public IThreadSafe + { + public: + virtual void Initialize(ImageBuffer3D& buffer, + ISeriesLoader& loader) = 0; + + virtual void Finalize() = 0; + + // Must return "true" if the thread has completed its task. Pay + // attention that this method can be invoked concurrently by + // several download threads. + virtual bool DownloadStep(bool& complete) = 0; + + virtual bool IsFullQualityAxial(size_t slice) = 0; + }; + + + private: + std::auto_ptr<ISeriesLoader> loader_; + std::auto_ptr<ImageBuffer3D> buffer_; + std::vector<boost::thread*> threads_; + bool started_; + bool continue_; + ObserversRegistry<ISliceableVolume> observers_; + bool loadingComplete_; + MessagingToolbox::Timestamp lastUpdate_; + std::auto_ptr<DicomDataset> referenceDataset_; + std::auto_ptr<IDownloadPolicy> policy_; + + std::auto_ptr<ParallelSlices> axialGeometry_; + std::auto_ptr<ParallelSlices> coronalGeometry_; + std::auto_ptr<ParallelSlices> sagittalGeometry_; + + void StoreUpdateTime(); + + void NotifyChange(bool force); + + static void LoadThread(VolumeImage* that); + + bool DetectProjection(VolumeProjection& projection, + bool& reverse, + const SliceGeometry& viewportSlice); + + const ParallelSlices& GetGeometryInternal(VolumeProjection projection); + + public: + VolumeImage(ISeriesLoader* loader); // Takes ownership + + virtual ~VolumeImage(); + + void SetDownloadPolicy(IDownloadPolicy* policy); // Takes ownership + + void SetThreadCount(size_t count); + + size_t GetThreadCount() const + { + return threads_.size(); + } + + virtual void Register(IChangeObserver& observer); + + virtual void Unregister(IChangeObserver& observer); + + virtual void Start(); + + virtual void Stop(); + + ParallelSlices* GetGeometry(VolumeProjection projection, + bool reverse); + + Vector GetVoxelDimensions(VolumeProjection projection) + { + return buffer_->GetVoxelDimensions(projection); + } + + bool IsLoadingComplete() const + { + return loadingComplete_; + } + + class LayerFactory : public ILayerRendererFactory + { + private: + VolumeImage& that_; + + public: + LayerFactory(VolumeImage& that) : + that_(that) + { + } + + virtual bool HasSourceVolume() const + { + return true; + } + + virtual ISliceableVolume& GetSourceVolume() const + { + return that_; + } + + virtual bool GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& viewportSlice); + + virtual ILayerRenderer* CreateLayerRenderer(const SliceGeometry& viewportSlice); + }; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/VolumeImagePolicyBase.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,72 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "VolumeImagePolicyBase.h" + +namespace OrthancStone +{ + VolumeImagePolicyBase::VolumeImagePolicyBase() : + buffer_(NULL), + loader_(NULL) + { + } + + + void VolumeImagePolicyBase::Initialize(ImageBuffer3D& buffer, + ISeriesLoader& loader) + { + if (buffer_ != NULL || + loader_ != NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + buffer_ = &buffer; + loader_ = &loader; + + InitializeInternal(buffer, loader); + } + + + bool VolumeImagePolicyBase::DownloadStep(bool& complete) + { + if (buffer_ == NULL || + loader_ == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + else + { + return DownloadStepInternal(complete, *buffer_, *loader_); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/VolumeImagePolicyBase.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,65 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "VolumeImage.h" + +namespace OrthancStone +{ + class VolumeImagePolicyBase : public VolumeImage::IDownloadPolicy + { + private: + ImageBuffer3D* buffer_; + ISeriesLoader* loader_; + + protected: + virtual void InitializeInternal(ImageBuffer3D& buffer, + ISeriesLoader& loader) = 0; + + virtual bool DownloadStepInternal(bool& complete, + ImageBuffer3D& buffer, + ISeriesLoader& loader) = 0; + + public: + VolumeImagePolicyBase(); + + virtual void Initialize(ImageBuffer3D& buffer, + ISeriesLoader& loader); + + virtual void Finalize() + { + } + + virtual bool DownloadStep(bool& complete); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/VolumeImageProgressivePolicy.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,213 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "VolumeImageProgressivePolicy.h" + +#include "../Toolbox/DownloadStack.h" +#include "../Orthanc/Core/Images/ImageProcessing.h" + +namespace OrthancStone +{ + class VolumeImageProgressivePolicy::AxialSlicesScheduler + { + private: + size_t depth_; + DownloadStack stack_; + + public: + AxialSlicesScheduler(size_t depth) : + depth_(depth), + stack_(3 * depth) // "3" stands for the number of quality levels + { + assert(depth > 0); + } + + void TagFullPriority(int z, + int neighborhood) + { + DownloadStack::Writer writer(stack_); + + // Also schedule the neighboring slices for download in medium quality + for (int offset = neighborhood; offset >= 1; offset--) + { + writer.SetTopNodePermissive((z + offset) + depth_ * Quality_Medium); + writer.SetTopNodePermissive((z - offset) + depth_ * Quality_Medium); + } + + writer.SetTopNodePermissive(z + depth_ * Quality_Full); + } + + bool LookupSlice(unsigned int& z, + Quality& quality) + { + unsigned int value; + if (stack_.Pop(value)) + { + z = value % depth_; + + switch (value / depth_) + { + case 0: + quality = Quality_Low; + break; + + case 1: + quality = Quality_Medium; + break; + + case 2: + quality = Quality_Full; + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + return true; + } + else + { + return false; + } + } + }; + + + bool VolumeImageProgressivePolicy::IsComplete() + { + boost::mutex::scoped_lock lock(qualityMutex_); + + for (size_t i = 0; i < axialSlicesQuality_.size(); i++) + { + if (axialSlicesQuality_[i] != Quality_Full) + { + return false; + } + } + + return true; + } + + + void VolumeImageProgressivePolicy::InitializeInternal(ImageBuffer3D& buffer, + ISeriesLoader& loader) + { + const size_t depth = loader.GetGeometry().GetSliceCount(); + + isJpegAvailable_ = loader.IsJpegAvailable(); + + axialSlicesQuality_.clear(); + axialSlicesQuality_.resize(depth, Quality_None); + scheduler_.reset(new AxialSlicesScheduler(depth)); + } + + + bool VolumeImageProgressivePolicy::DownloadStepInternal(bool& complete, + ImageBuffer3D& buffer, + ISeriesLoader& loader) + { + unsigned int z; + Quality quality; + + if (!scheduler_->LookupSlice(z, quality)) + { + // There is no more frame to be downloaded. Before stopping, + // each loader thread checks whether all the frames have been + // downloaded at maximum quality. + complete = IsComplete(); + return true; + } + + if (quality != Quality_Full && + !isJpegAvailable_) + { + // Cannot fulfill this command, as progressive JPEG download + // is unavailable (i.e. the Web viewer plugin is unavailable) + return false; + } + + std::auto_ptr<Orthanc::ImageAccessor> frame; + + try + { + switch (quality) + { + case Quality_Low: + frame.reset(loader.DownloadJpegFrame(z, 10)); + break; + + case Quality_Medium: + frame.reset(loader.DownloadJpegFrame(z, 90)); + break; + + case Quality_Full: + frame.reset(loader.DownloadFrame(z)); + break; + + default: + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + catch (Orthanc::OrthancException&) + { + // The Orthanc server cannot decode this instance + return false; + } + + if (frame.get() != NULL) + { + boost::mutex::scoped_lock lock(qualityMutex_); + + if (axialSlicesQuality_[z] == Quality_None || + axialSlicesQuality_[z] < quality) + { + axialSlicesQuality_[z] = quality; + + ImageBuffer3D::SliceWriter writer(buffer, VolumeProjection_Axial, z); + Orthanc::ImageProcessing::Convert(writer.GetAccessor(), *frame); + } + } + + return false; + } + + + bool VolumeImageProgressivePolicy::IsFullQualityAxial(size_t slice) + { + scheduler_->TagFullPriority(slice, 3); + + { + boost::mutex::scoped_lock lock(qualityMutex_); + return (axialSlicesQuality_[slice] == Quality_Full); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/VolumeImageProgressivePolicy.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,70 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "VolumeImagePolicyBase.h" + +namespace OrthancStone +{ + class VolumeImageProgressivePolicy : public VolumeImagePolicyBase + { + private: + enum Quality + { + Quality_Low = 0, + Quality_Medium = 1, + Quality_Full = 2, + Quality_None = 3 + }; + + class AxialSlicesScheduler; + + std::auto_ptr<AxialSlicesScheduler> scheduler_; + boost::mutex qualityMutex_; + std::vector<Quality> axialSlicesQuality_; + bool isJpegAvailable_; + + bool IsComplete(); + + protected: + virtual void InitializeInternal(ImageBuffer3D& buffer, + ISeriesLoader& loader); + + virtual bool DownloadStepInternal(bool& complete, + ImageBuffer3D& buffer, + ISeriesLoader& loader); + + public: + virtual bool IsFullQualityAxial(size_t slice); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/VolumeImageSimplePolicy.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,118 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "VolumeImageSimplePolicy.h" + +#include "../Orthanc/Core/Images/ImageProcessing.h" + +namespace OrthancStone +{ + void VolumeImageSimplePolicy::InitializeInternal(ImageBuffer3D& buffer, + ISeriesLoader& loader) + { + boost::mutex::scoped_lock lock(mutex_); + + const size_t depth = loader.GetGeometry().GetSliceCount(); + pendingSlices_.clear(); + + for (size_t i = 0; i < depth; i++) + { + pendingSlices_.insert(i); + } + + doneSlices_.clear(); + doneSlices_.resize(depth, false); + } + + + bool VolumeImageSimplePolicy::DownloadStepInternal(bool& complete, + ImageBuffer3D& buffer, + ISeriesLoader& loader) + { + size_t slice; + + { + boost::mutex::scoped_lock lock(mutex_); + + if (pendingSlices_.empty()) + { + return true; + } + else + { + slice = *pendingSlices_.begin(); + pendingSlices_.erase(slice); + } + } + + std::auto_ptr<Orthanc::ImageAccessor> frame; + + try + { + frame.reset(loader.DownloadFrame(slice)); + } + catch (Orthanc::OrthancException&) + { + // The Orthanc server cannot decode this instance + return false; + } + + if (frame.get() != NULL) + { + { + ImageBuffer3D::SliceWriter writer(buffer, VolumeProjection_Axial, slice); + Orthanc::ImageProcessing::Convert(writer.GetAccessor(), *frame); + } + + { + boost::mutex::scoped_lock lock(mutex_); + + doneSlices_[slice] = true; + + if (pendingSlices_.empty()) + { + complete = true; + return true; + } + } + } + + return false; + } + + + bool VolumeImageSimplePolicy::IsFullQualityAxial(size_t slice) + { + boost::mutex::scoped_lock lock(mutex_); + return doneSlices_[slice]; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Volumes/VolumeImageSimplePolicy.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,57 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "VolumeImagePolicyBase.h" + +namespace OrthancStone +{ + class VolumeImageSimplePolicy : public VolumeImagePolicyBase + { + private: + boost::mutex mutex_; + std::set<size_t> pendingSlices_; + std::vector<bool> doneSlices_; + + protected: + virtual void InitializeInternal(ImageBuffer3D& buffer, + ISeriesLoader& loader); + + virtual bool DownloadStepInternal(bool& complete, + ImageBuffer3D& buffer, + ISeriesLoader& loader); + + public: + virtual bool IsFullQualityAxial(size_t slice); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/CairoWidget.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,105 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "CairoWidget.h" + +#include "../Orthanc/Core/Images/ImageProcessing.h" +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + static bool IsAligned(const Orthanc::ImageAccessor& target) + { + // TODO + return true; + } + + + void CairoWidget::SetSize(unsigned int width, + unsigned int height) + { + surface_.SetSize(width, height); + } + + + bool CairoWidget::Render(Orthanc::ImageAccessor& target) + { + // Don't call the base class here, as + // "ClearBackgroundCairo()" is a faster alternative + + if (IsAligned(target)) + { + CairoSurface surface(target); + CairoContext context(surface); + ClearBackgroundCairo(context); + return RenderCairo(context); + } + else + { + CairoContext context(surface_); + ClearBackgroundCairo(context); + + if (RenderCairo(context)) + { + Orthanc::ImageProcessing::Copy(target, surface_.GetAccessor()); + return true; + } + else + { + return false; + } + } + } + + + void CairoWidget::RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y) + { + if (IsAligned(target)) + { + CairoSurface surface(target); + CairoContext context(surface); + RenderMouseOverCairo(context, x, y); + } + else + { + Orthanc::ImageAccessor accessor = surface_.GetAccessor(); + Orthanc::ImageProcessing::Copy(accessor, target); + + CairoContext context(surface_); + RenderMouseOverCairo(context, x, y); + + Orthanc::ImageProcessing::Copy(target, accessor); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/CairoWidget.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,61 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "WidgetBase.h" + +namespace OrthancStone +{ + class CairoWidget : public WidgetBase + { + private: + CairoSurface surface_; + + protected: + virtual bool RenderCairo(CairoContext& context) = 0; + + virtual void RenderMouseOverCairo(CairoContext& context, + int x, + int y) = 0; + + public: + virtual void SetSize(unsigned int width, + unsigned int height); + + virtual bool Render(Orthanc::ImageAccessor& target); + + virtual void RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/EmptyWidget.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,45 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "EmptyWidget.h" + +#include "../Orthanc/Core/Images/ImageProcessing.h" + +namespace OrthancStone +{ + bool EmptyWidget::Render(Orthanc::ImageAccessor& surface) + { + // Note: This call is slow + Orthanc::ImageProcessing::Set(surface, red_, green_, blue_, 255); + return true; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/EmptyWidget.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,117 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "IWidget.h" + +namespace OrthancStone +{ + /** + * This is a test widget that simply fills its surface with an + * uniform color. + **/ + class EmptyWidget : public IWidget + { + private: + uint8_t red_; + uint8_t green_; + uint8_t blue_; + + public: + EmptyWidget(uint8_t red, + uint8_t green, + uint8_t blue) : + red_(red), + green_(green), + blue_(blue) + { + } + + virtual void SetStatusBar(IStatusBar& statusBar) + { + } + + virtual void ResetStatusBar() + { + } + + virtual void Register(IChangeObserver& observer) + { + } + + virtual void Unregister(IChangeObserver& observer) + { + } + + virtual void Start() + { + } + + virtual void Stop() + { + } + + virtual void SetSize(unsigned int width, + unsigned int height) + { + } + + virtual bool Render(Orthanc::ImageAccessor& surface); + + virtual IMouseTracker* CreateMouseTracker(MouseButton button, + int x, + int y, + KeyboardModifiers modifiers) + { + return NULL; + } + + virtual void RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y) + { + } + + virtual void MouseWheel(MouseWheelDirection direction, + int x, + int y, + KeyboardModifiers modifiers) + { + } + + virtual void KeyPressed(char key, + KeyboardModifiers modifiers) + { + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/IWidget.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,88 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Enumerations.h" +#include "../Viewport/IMouseTracker.h" +#include "../Viewport/IStatusBar.h" + +namespace OrthancStone +{ + class IWidget : public IThreadUnsafe + { + public: + class IChangeObserver : public boost::noncopyable + { + public: + virtual ~IChangeObserver() + { + } + + virtual void NotifyChange(const IWidget& widget) = 0; + }; + + virtual void SetStatusBar(IStatusBar& statusBar) = 0; + + virtual void ResetStatusBar() = 0; + + virtual void Register(IChangeObserver& observer) = 0; + + virtual void Unregister(IChangeObserver& observer) = 0; + + virtual void Start() = 0; + + virtual void Stop() = 0; + + virtual void SetSize(unsigned int width, + unsigned int height) = 0; + + virtual bool Render(Orthanc::ImageAccessor& surface) = 0; + + virtual IMouseTracker* CreateMouseTracker(MouseButton button, + int x, + int y, + KeyboardModifiers modifiers) = 0; + + virtual void RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y) = 0; + + virtual void MouseWheel(MouseWheelDirection direction, + int x, + int y, + KeyboardModifiers modifiers) = 0; + + virtual void KeyPressed(char key, + KeyboardModifiers modifiers) = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/IWorldSceneInteractor.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,75 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "IWorldSceneMouseTracker.h" + +#include "../Toolbox/SliceGeometry.h" +#include "../Toolbox/ViewportGeometry.h" +#include "../Enumerations.h" +#include "../Viewport/IStatusBar.h" + +namespace OrthancStone +{ + class WorldSceneWidget; + + class IWorldSceneInteractor : public IThreadSafe + { + public: + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + MouseButton button, + double x, + double y, + IStatusBar* statusBar) = 0; + + virtual void MouseOver(CairoContext& context, + WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) = 0; + + virtual void MouseWheel(WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + IStatusBar* statusBar) = 0; + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/IWorldSceneMouseTracker.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,51 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Toolbox/IThreadSafety.h" +#include "../Viewport/CairoContext.h" + +namespace OrthancStone +{ + class IWorldSceneMouseTracker : public IThreadUnsafe + { + public: + virtual void Render(CairoContext& context, + double zoom) = 0; + + virtual void MouseUp() = 0; + + virtual void MouseMove(double x, + double y) = 0; + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/LayeredSceneWidget.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,637 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#define _USE_MATH_DEFINES // To access M_PI in Visual Studio +#include <cmath> + +#include "LayeredSceneWidget.h" + +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + class LayeredSceneWidget::Renderers : public boost::noncopyable + { + private: + boost::mutex mutex_; + std::vector<ILayerRenderer*> renderers_; + std::vector<bool> assigned_; + + void Assign(size_t index, + ILayerRenderer* renderer) + { + if (index >= renderers_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + if (renderers_[index] != NULL) + { + delete renderers_[index]; + } + + renderers_[index] = renderer; + assigned_[index] = true; + } + + public: + Renderers(size_t size) + { + renderers_.resize(size); + assigned_.resize(size, false); + } + + ~Renderers() + { + for (size_t i = 0; i < renderers_.size(); i++) + { + Assign(i, NULL); + } + } + + static void Merge(Renderers& target, + Renderers& source) + { + boost::mutex::scoped_lock lockSource(source.mutex_); + boost::mutex::scoped_lock lockTarget(target.mutex_); + + size_t count = target.renderers_.size(); + if (count != source.renderers_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + for (size_t i = 0; i < count; i++) + { + if (source.assigned_[i]) + { + target.Assign(i, source.renderers_[i]); // Transfers ownership + source.renderers_[i] = NULL; + source.assigned_[i] = false; + } + } + } + + void SetRenderer(size_t index, + ILayerRenderer* renderer) // Takes ownership + { + boost::mutex::scoped_lock lock(mutex_); + Assign(index, renderer); + } + + bool RenderScene(CairoContext& context, + const ViewportGeometry& view) + { + boost::mutex::scoped_lock lock(mutex_); + + bool fullQuality = true; + + for (size_t i = 0; i < renderers_.size(); i++) + { + if (renderers_[i] != NULL && + !renderers_[i]->RenderLayer(context, view)) + { + return false; + } + + if (renderers_[i] != NULL && + !renderers_[i]->IsFullQuality()) + { + fullQuality = false; + } + } + + if (!fullQuality) + { + double x, y; + view.MapDisplayToScene(x, y, static_cast<double>(view.GetDisplayWidth()) / 2.0, 10); + + cairo_t *cr = context.GetObject(); + cairo_translate(cr, x, y); + cairo_arc(cr, 0, 0, 5.0 / view.GetZoom(), 0, 2 * M_PI); + cairo_set_line_width(cr, 2.0 / view.GetZoom()); + cairo_set_source_rgb(cr, 1, 1, 1); + cairo_stroke_preserve(cr); + cairo_set_source_rgb(cr, 1, 0, 0); + cairo_fill(cr); + } + + return true; + } + + void SetLayerStyle(size_t index, + const RenderStyle& style) + { + boost::mutex::scoped_lock lock(mutex_); + + if (renderers_[index] != NULL) + { + renderers_[index]->SetLayerStyle(style); + } + } + }; + + + + class LayeredSceneWidget::PendingLayers : public boost::noncopyable + { + private: + boost::mutex mutex_; + boost::condition_variable elementAvailable_; + size_t layerCount_; + std::list<size_t> queue_; + std::vector<bool> layersToUpdate_; + bool continue_; + + void TagAllLayers() + { + queue_.clear(); + + for (unsigned int i = 0; i < layerCount_; i++) + { + queue_.push_back(i); + layersToUpdate_[i] = true; + } + + if (layerCount_ != 0) + { + elementAvailable_.notify_one(); + } + } + + public: + PendingLayers() : + layerCount_(0), + continue_(true) + { + } + + void Stop() + { + continue_ = false; + elementAvailable_.notify_one(); + } + + void SetLayerCount(size_t count) + { + boost::mutex::scoped_lock lock(mutex_); + + layerCount_ = count; + layersToUpdate_.resize(count); + + TagAllLayers(); + } + + void InvalidateAllLayers() + { + boost::mutex::scoped_lock lock(mutex_); + TagAllLayers(); + } + + void InvalidateLayer(size_t layer) + { + boost::mutex::scoped_lock lock(mutex_); + + if (layer < layerCount_) + { + if (layersToUpdate_[layer]) + { + // The layer is already scheduled for update, ignore this + // invalidation + } + else + { + queue_.push_back(layer); + layersToUpdate_[layer] = true; + elementAvailable_.notify_one(); + } + } + } + + bool Dequeue(size_t& layer, + bool& isLast) + { + boost::mutex::scoped_lock lock(mutex_); + + // WARNING: Do NOT use "timed_wait" on condition variables, as + // sleeping is not properly supported by Boost for Google NaCl + while (queue_.empty() && + continue_) + { + elementAvailable_.wait(lock); + } + + if (!continue_) + { + return false; + } + + layer = queue_.front(); + layersToUpdate_[layer] = false; + queue_.pop_front(); + + isLast = queue_.empty(); + + return true; + } + }; + + + class LayeredSceneWidget::Layer : public ISliceableVolume::IChangeObserver + { + private: + boost::mutex mutex_; + std::auto_ptr<ILayerRendererFactory> factory_; + PendingLayers& layers_; + size_t index_; + std::auto_ptr<RenderStyle> style_; + + public: + Layer(ILayerRendererFactory* factory, + PendingLayers& layers, + size_t index) : + factory_(factory), + layers_(layers), + index_(index) + { + if (factory == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + } + + virtual void NotifyChange(const OrthancStone::ISliceableVolume&) + { + layers_.InvalidateLayer(index_); + } + + void Start() + { + if (factory_->HasSourceVolume()) + { + factory_->GetSourceVolume().Register(*this); + } + } + + void Stop() + { + if (factory_->HasSourceVolume()) + { + factory_->GetSourceVolume().Unregister(*this); + } + } + + bool GetExtent(double& x1, + double& y1, + double& x2, + double& y2, + const SliceGeometry& displaySlice) + { + boost::mutex::scoped_lock lock(mutex_); + assert(factory_.get() != NULL); + return factory_->GetExtent(x1, y1, x2, y2, displaySlice); + } + + RenderStyle GetStyle() + { + boost::mutex::scoped_lock lock(mutex_); + + if (style_.get() == NULL) + { + return RenderStyle(); + } + else + { + return *style_; + } + } + + void SetStyle(const RenderStyle& style) + { + boost::mutex::scoped_lock lock(mutex_); + style_.reset(new RenderStyle(style)); + } + + + ILayerRenderer* CreateRenderer(const SliceGeometry& displaySlice) + { + boost::mutex::scoped_lock lock(mutex_); + assert(factory_.get() != NULL); + + std::auto_ptr<ILayerRenderer> renderer(factory_->CreateLayerRenderer(displaySlice)); + + if (renderer.get() != NULL && + style_.get() != NULL) + { + renderer->SetLayerStyle(*style_); + } + + return renderer.release(); + } + }; + + + + SliceGeometry LayeredSceneWidget::GetSlice() + { + boost::mutex::scoped_lock lock(sliceMutex_); + return slice_; + } + + + void LayeredSceneWidget::UpdateStep() + { + size_t layer = 0; + bool isLast = true; + if (!pendingLayers_->Dequeue(layer, isLast)) + { + return; + } + + SliceGeometry slice = GetSlice(); + + std::auto_ptr<ILayerRenderer> renderer; + renderer.reset(layers_[layer]->CreateRenderer(slice)); + + if (renderer.get() != NULL) + { + pendingRenderers_->SetRenderer(layer, renderer.release()); + } + else + { + pendingRenderers_->SetRenderer(layer, NULL); + } + + if (isLast) + { + Renderers::Merge(*renderers_, *pendingRenderers_); + NotifyChange(); + } + } + + + bool LayeredSceneWidget::RenderScene(CairoContext& context, + const ViewportGeometry& view) + { + assert(IsStarted()); + return renderers_->RenderScene(context, view); + } + + + LayeredSceneWidget::LayeredSceneWidget() + { + pendingLayers_.reset(new PendingLayers); + SetBackgroundCleared(true); + } + + + LayeredSceneWidget::~LayeredSceneWidget() + { + for (size_t i = 0; i < layers_.size(); i++) + { + assert(layers_[i] != NULL); + delete layers_[i]; + } + } + + + void LayeredSceneWidget::GetSceneExtent(double& x1, + double& y1, + double& x2, + double& y2) + { + boost::mutex::scoped_lock lock(sliceMutex_); + + bool first = true; + + for (size_t i = 0; i < layers_.size(); i++) + { + double ax, ay, bx, by; + + assert(layers_[i] != NULL); + if (layers_[i]->GetExtent(ax, ay, bx, by, slice_)) + { + if (ax > bx) + { + std::swap(ax, bx); + } + + if (ay > by) + { + std::swap(ay, by); + } + + if (first) + { + x1 = ax; + y1 = ay; + x2 = bx; + y2 = by; + first = false; + } + else + { + x1 = std::min(x1, ax); + y1 = std::min(y1, ay); + x2 = std::max(x2, bx); + y2 = std::max(y2, by); + } + } + } + + if (first) + { + x1 = -1; + y1 = -1; + x2 = 1; + y2 = 1; + } + + // Ensure the extent is non-empty + if (x1 >= x2) + { + double tmp = x1; + x1 = tmp - 0.5; + x2 = tmp + 0.5; + } + + if (y1 >= y2) + { + double tmp = y1; + y1 = tmp - 0.5; + y2 = tmp + 0.5; + } + } + + + + ILayerRendererFactory& LayeredSceneWidget::AddLayer(size_t& layerIndex, + ILayerRendererFactory* factory) + { + if (IsStarted()) + { + // Start() has already been invoked + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + layerIndex = layers_.size(); + layers_.push_back(new Layer(factory, *pendingLayers_, layers_.size())); + + return *factory; + } + + + void LayeredSceneWidget::AddLayer(ILayerRendererFactory* factory) + { + size_t layerIndex; // Ignored + AddLayer(layerIndex, factory); + } + + + RenderStyle LayeredSceneWidget::GetLayerStyle(size_t layer) + { + if (layer >= layers_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + return layers_[layer]->GetStyle(); + } + + + void LayeredSceneWidget::SetLayerStyle(size_t layer, + const RenderStyle& style) + { + if (layer >= layers_.size()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + layers_[layer]->SetStyle(style); + + if (renderers_.get() != NULL) + { + renderers_->SetLayerStyle(layer, style); + } + + InvalidateLayer(layer); + } + + + + struct LayeredSceneWidget::SliceChangeFunctor + { + const SliceGeometry& slice_; + + SliceChangeFunctor(const SliceGeometry& slice) : + slice_(slice) + { + } + + void operator() (ISliceObserver& observer, + const LayeredSceneWidget& source) + { + observer.NotifySliceChange(source, slice_); + } + }; + + + void LayeredSceneWidget::SetSlice(const SliceGeometry& slice) + { + { + boost::mutex::scoped_lock lock(sliceMutex_); + slice_ = slice; + } + + InvalidateAllLayers(); + + SliceChangeFunctor functor(slice); + observers_.Notify(this, functor); + } + + + void LayeredSceneWidget::InvalidateLayer(unsigned int layer) + { + pendingLayers_->InvalidateLayer(layer); + //NotifyChange(); // TODO Understand why this makes the SDL engine not update the display subsequently + } + + + void LayeredSceneWidget::InvalidateAllLayers() + { + pendingLayers_->InvalidateAllLayers(); + //NotifyChange(); // TODO Understand why this makes the SDL engine not update the display subsequently + } + + + void LayeredSceneWidget::Start() + { + if (IsStarted()) + { + // Start() has already been invoked + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + for (size_t i = 0; i < layers_.size(); i++) + { + layers_[i]->Start(); + } + + renderers_.reset(new Renderers(layers_.size())); + pendingRenderers_.reset(new Renderers(layers_.size())); + + pendingLayers_->SetLayerCount(layers_.size()); + + WorldSceneWidget::Start(); + } + + + void LayeredSceneWidget::Stop() + { + if (!IsStarted()) + { + // Stop() has already been invoked + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + pendingLayers_->Stop(); + WorldSceneWidget::Stop(); + + renderers_.reset(NULL); + pendingRenderers_.reset(NULL); + + for (size_t i = 0; i < layers_.size(); i++) + { + layers_[i]->Stop(); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/LayeredSceneWidget.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,135 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "WorldSceneWidget.h" + +#include "../Layers/ILayerRendererFactory.h" + + +namespace OrthancStone +{ + class LayeredSceneWidget : public WorldSceneWidget + { + public: + // Must be thread-safe + class ISliceObserver : public boost::noncopyable + { + public: + virtual ~ISliceObserver() + { + } + + virtual void NotifySliceChange(const LayeredSceneWidget& source, + const SliceGeometry& slice) = 0; + }; + + private: + struct SliceChangeFunctor; + class Renderers; + class PendingLayers; + class Layer; + + typedef ObserversRegistry<LayeredSceneWidget, ISliceObserver> Observers; + + std::vector<Layer*> layers_; + std::auto_ptr<Renderers> renderers_; + std::auto_ptr<PendingLayers> pendingLayers_; + std::auto_ptr<Renderers> pendingRenderers_; + boost::mutex sliceMutex_; + SliceGeometry slice_; + Observers observers_; + + protected: + virtual bool HasUpdateThread() const + { + return true; + } + + virtual void UpdateStep(); + + virtual bool RenderScene(CairoContext& context, + const ViewportGeometry& view); + + public: + LayeredSceneWidget(); + + virtual ~LayeredSceneWidget(); + + virtual SliceGeometry GetSlice(); + + virtual void GetSceneExtent(double& x1, + double& y1, + double& x2, + double& y2); + + ILayerRendererFactory& AddLayer(size_t& layerIndex, + ILayerRendererFactory* factory); // Takes ownership + + // Simpler version for basic use cases + void AddLayer(ILayerRendererFactory* factory); // Takes ownership + + size_t GetLayerCount() const + { + return layers_.size(); + } + + RenderStyle GetLayerStyle(size_t layer); + + void SetLayerStyle(size_t layer, + const RenderStyle& style); + + void SetSlice(const SliceGeometry& slice); + + void InvalidateLayer(unsigned int layer); + + void InvalidateAllLayers(); + + virtual void Start(); + + virtual void Stop(); + + using WorldSceneWidget::Register; + using WorldSceneWidget::Unregister; + + void Register(ISliceObserver& observer) + { + observers_.Register(observer); + } + + void Unregister(ISliceObserver& observer) + { + observers_.Unregister(observer); + } + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/LayoutWidget.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,493 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "LayoutWidget.h" + +#include "../Orthanc/Core/Logging.h" +#include "../Orthanc/Core/OrthancException.h" + +#include <boost/math/special_functions/round.hpp> + +namespace OrthancStone +{ + class LayoutWidget::LayoutMouseTracker : public IMouseTracker + { + private: + std::auto_ptr<IMouseTracker> tracker_; + int left_; + int top_; + unsigned int width_; + unsigned int height_; + + public: + LayoutMouseTracker(IMouseTracker* tracker, + int left, + int top, + unsigned int width, + unsigned int height) : + tracker_(tracker), + left_(left), + top_(top), + width_(width), + height_(height) + { + if (tracker == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + } + + virtual void Render(Orthanc::ImageAccessor& surface) + { + Orthanc::ImageAccessor accessor = surface.GetRegion(left_, top_, width_, height_); + tracker_->Render(accessor); + } + + virtual void MouseUp() + { + tracker_->MouseUp(); + } + + virtual void MouseMove(int x, + int y) + { + tracker_->MouseMove(x - left_, y - top_); + } + }; + + + class LayoutWidget::ChildWidget : public boost::noncopyable + { + private: + std::auto_ptr<IWidget> widget_; + int left_; + int top_; + unsigned int width_; + unsigned int height_; + + public: + ChildWidget(IWidget* widget) : + widget_(widget) + { + assert(widget != NULL); + SetEmpty(); + } + + IWidget& GetWidget() const + { + return *widget_; + } + + void SetRectangle(unsigned int left, + unsigned int top, + unsigned int width, + unsigned int height) + { + left_ = left; + top_ = top; + width_ = width; + height_ = height; + + widget_->SetSize(width, height); + } + + void SetEmpty() + { + SetRectangle(0, 0, 0, 0); + } + + bool Contains(int x, + int y) const + { + return (x >= left_ && + y >= top_ && + x < left_ + static_cast<int>(width_) && + y < top_ + static_cast<int>(height_)); + } + + bool Render(Orthanc::ImageAccessor& target) + { + if (width_ == 0 || + height_ == 0) + { + return true; + } + else + { + Orthanc::ImageAccessor accessor = target.GetRegion(left_, top_, width_, height_); + return widget_->Render(accessor); + } + } + + IMouseTracker* CreateMouseTracker(MouseButton button, + int x, + int y, + KeyboardModifiers modifiers) + { + if (Contains(x, y)) + { + IMouseTracker* tracker = widget_->CreateMouseTracker(button, + x - left_, + y - top_, + modifiers); + if (tracker) + { + return new LayoutMouseTracker(tracker, left_, top_, width_, height_); + } + } + + return NULL; + } + + void RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y) + { + if (Contains(x, y)) + { + Orthanc::ImageAccessor accessor = target.GetRegion(left_, top_, width_, height_); + + widget_->RenderMouseOver(accessor, x - left_, y - top_); + } + } + + void MouseWheel(MouseWheelDirection direction, + int x, + int y, + KeyboardModifiers modifiers) + { + if (Contains(x, y)) + { + widget_->MouseWheel(direction, x - left_, y - top_, modifiers); + } + } + }; + + + void LayoutWidget::ComputeChildrenExtents() + { + if (children_.size() == 0) + { + return; + } + + float internal = static_cast<float>(paddingInternal_); + + if (width_ <= paddingLeft_ + paddingRight_ || + height_ <= paddingTop_ + paddingBottom_) + { + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->SetEmpty(); + } + } + else if (isHorizontal_) + { + unsigned int padding = paddingLeft_ + paddingRight_ + (children_.size() - 1) * paddingInternal_; + float childWidth = ((static_cast<float>(width_) - static_cast<float>(padding)) / + static_cast<float>(children_.size())); + + for (size_t i = 0; i < children_.size(); i++) + { + float left = static_cast<float>(paddingLeft_) + static_cast<float>(i) * (childWidth + internal); + float right = left + childWidth; + + if (left >= right) + { + children_[i]->SetEmpty(); + } + else + { + children_[i]->SetRectangle(static_cast<unsigned int>(left), + paddingTop_, + boost::math::iround(right - left), + height_ - paddingTop_ - paddingBottom_); + } + } + } + else + { + unsigned int padding = paddingTop_ + paddingBottom_ + (children_.size() - 1) * paddingInternal_; + float childHeight = ((static_cast<float>(height_) - static_cast<float>(padding)) / + static_cast<float>(children_.size())); + + for (size_t i = 0; i < children_.size(); i++) + { + float top = static_cast<float>(paddingTop_) + static_cast<float>(i) * (childHeight + internal); + float bottom = top + childHeight; + + if (top >= bottom) + { + children_[i]->SetEmpty(); + } + else + { + children_[i]->SetRectangle(paddingTop_, + static_cast<unsigned int>(top), + width_ - paddingLeft_ - paddingRight_, + boost::math::iround(bottom - top)); + } + } + } + + NotifyChange(*this); + } + + + void LayoutWidget::UpdateStep() + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + LayoutWidget::LayoutWidget() : + isHorizontal_(true), + started_(false), + width_(0), + height_(0), + paddingLeft_(0), + paddingTop_(0), + paddingRight_(0), + paddingBottom_(0), + paddingInternal_(0) + { + } + + + LayoutWidget::~LayoutWidget() + { + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->GetWidget().Unregister(*this); + delete children_[i]; + } + } + + + void LayoutWidget::NotifyChange(const IWidget& widget) + { + // One of the children has changed + WidgetBase::NotifyChange(); + } + + + void LayoutWidget::SetHorizontal() + { + isHorizontal_ = true; + ComputeChildrenExtents(); + } + + + void LayoutWidget::SetVertical() + { + isHorizontal_ = false; + ComputeChildrenExtents(); + } + + + void LayoutWidget::SetPadding(unsigned int left, + unsigned int top, + unsigned int right, + unsigned int bottom, + unsigned int spacing) + { + paddingLeft_ = left; + paddingTop_ = top; + paddingRight_ = right; + paddingBottom_ = bottom; + paddingInternal_ = spacing; + } + + + void LayoutWidget::SetPadding(unsigned int padding) + { + paddingLeft_ = padding; + paddingTop_ = padding; + paddingRight_ = padding; + paddingBottom_ = padding; + paddingInternal_ = padding; + } + + + IWidget& LayoutWidget::AddWidget(IWidget* widget) // Takes ownership + { + if (started_) + { + LOG(ERROR) << "Cannot add child once Start() has been invoked"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + if (widget == NULL) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + if (GetStatusBar() != NULL) + { + widget->SetStatusBar(*GetStatusBar()); + } + else + { + widget->ResetStatusBar(); + } + + children_.push_back(new ChildWidget(widget)); + widget->Register(*this); + + ComputeChildrenExtents(); + + return *widget; + } + + + void LayoutWidget::SetStatusBar(IStatusBar& statusBar) + { + WidgetBase::SetStatusBar(statusBar); + + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->GetWidget().SetStatusBar(statusBar); + } + } + + + void LayoutWidget::ResetStatusBar() + { + WidgetBase::ResetStatusBar(); + + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->GetWidget().ResetStatusBar(); + } + } + + + void LayoutWidget::Start() + { + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->GetWidget().Start(); + } + + WidgetBase::Start(); + } + + + void LayoutWidget::Stop() + { + WidgetBase::Stop(); + + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->GetWidget().Stop(); + } + } + + + void LayoutWidget::SetSize(unsigned int width, + unsigned int height) + { + width_ = width; + height_ = height; + ComputeChildrenExtents(); + } + + + bool LayoutWidget::Render(Orthanc::ImageAccessor& surface) + { + if (!WidgetBase::Render(surface)) + { + return false; + } + + for (size_t i = 0; i < children_.size(); i++) + { + if (!children_[i]->Render(surface)) + { + return false; + } + } + + return true; + } + + + IMouseTracker* LayoutWidget::CreateMouseTracker(MouseButton button, + int x, + int y, + KeyboardModifiers modifiers) + { + for (size_t i = 0; i < children_.size(); i++) + { + IMouseTracker* tracker = children_[i]->CreateMouseTracker(button, x, y, modifiers); + if (tracker != NULL) + { + return tracker; + } + } + + return NULL; + } + + + void LayoutWidget::RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y) + { + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->RenderMouseOver(target, x, y); + } + } + + + void LayoutWidget::MouseWheel(MouseWheelDirection direction, + int x, + int y, + KeyboardModifiers modifiers) + { + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->MouseWheel(direction, x, y, modifiers); + } + } + + + void LayoutWidget::KeyPressed(char key, + KeyboardModifiers modifiers) + { + for (size_t i = 0; i < children_.size(); i++) + { + children_[i]->GetWidget().KeyPressed(key, modifiers); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/LayoutWidget.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,147 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "WidgetBase.h" + + +namespace OrthancStone +{ + class LayoutWidget : + public WidgetBase, + public IWidget::IChangeObserver + { + private: + class LayoutMouseTracker; + class ChildWidget; + + std::vector<ChildWidget*> children_; + bool isHorizontal_; + bool started_; + unsigned int width_; + unsigned int height_; + std::auto_ptr<IMouseTracker> mouseTracker_; + unsigned int paddingLeft_; + unsigned int paddingTop_; + unsigned int paddingRight_; + unsigned int paddingBottom_; + unsigned int paddingInternal_; + + void ComputeChildrenExtents(); + + protected: + virtual bool HasUpdateThread() const + { + return false; + } + + virtual void UpdateStep(); + + + public: + LayoutWidget(); + + virtual ~LayoutWidget(); + + virtual void NotifyChange(const IWidget& widget); + + void SetHorizontal(); + + void SetVertical(); + + void SetPadding(unsigned int left, + unsigned int top, + unsigned int right, + unsigned int bottom, + unsigned int spacing); + + void SetPadding(unsigned int padding); + + unsigned int GetPaddingLeft() const + { + return paddingLeft_; + } + + unsigned int GetPaddingTop() const + { + return paddingTop_; + } + + unsigned int GetPaddingRight() const + { + return paddingRight_; + } + + unsigned int GetPaddingBottom() const + { + return paddingBottom_; + } + + unsigned int GetPaddingInternal() const + { + return paddingInternal_; + } + + IWidget& AddWidget(IWidget* widget); // Takes ownership + + virtual void SetStatusBar(IStatusBar& statusBar); + + virtual void ResetStatusBar(); + + virtual void Start(); + + virtual void Stop(); + + virtual void SetSize(unsigned int width, + unsigned int height); + + virtual bool Render(Orthanc::ImageAccessor& surface); + + virtual IMouseTracker* CreateMouseTracker(MouseButton button, + int x, + int y, + KeyboardModifiers modifiers); + + virtual void RenderMouseOver(Orthanc::ImageAccessor& target, + int x, + int y); + + virtual void MouseWheel(MouseWheelDirection direction, + int x, + int y, + KeyboardModifiers modifiers); + + virtual void KeyPressed(char key, + KeyboardModifiers modifiers); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/TestCairoWidget.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,138 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "TestCairoWidget.h" + +#include "../Orthanc/Core/Toolbox.h" + +#include <stdio.h> + + +namespace OrthancStone +{ + namespace Samples + { + void TestCairoWidget::UpdateStep() + { + value_ -= 0.01f; + if (value_ < 0) + { + value_ = 1; + } + + NotifyChange(); + + Orthanc::Toolbox::USleep(25000); + } + + + bool TestCairoWidget::RenderCairo(CairoContext& context) + { + cairo_t* cr = context.GetObject(); + + cairo_set_source_rgb (cr, .3, 0, 0); + cairo_paint(cr); + + cairo_set_source_rgb(cr, 0, 1, 0); + cairo_rectangle(cr, width_ / 4, height_ / 4, width_ / 2, height_ / 2); + cairo_set_line_width(cr, 1.0); + cairo_fill(cr); + + cairo_set_source_rgb(cr, 0, 1, value_); + cairo_rectangle(cr, width_ / 2 - 50, height_ / 2 - 50, 100, 100); + cairo_fill(cr); + + return true; + } + + + void TestCairoWidget::RenderMouseOverCairo(CairoContext& context, + int x, + int y) + { + cairo_t* cr = context.GetObject(); + + cairo_set_source_rgb (cr, 1, 0, 0); + cairo_rectangle(cr, x - 5, y - 5, 10, 10); + cairo_set_line_width(cr, 1.0); + cairo_stroke(cr); + + char buf[64]; + sprintf(buf, "(%d,%d)", x, y); + UpdateStatusBar(buf); + } + + + TestCairoWidget::TestCairoWidget(bool animate) : + width_(0), + height_(0), + value_(1), + animate_(animate) + { + } + + + void TestCairoWidget::SetSize(unsigned int width, + unsigned int height) + { + CairoWidget::SetSize(width, height); + width_ = width; + height_ = height; + } + + + IMouseTracker* TestCairoWidget::CreateMouseTracker(MouseButton button, + int x, + int y, + KeyboardModifiers modifiers) + { + UpdateStatusBar("Click"); + return NULL; + } + + + void TestCairoWidget::MouseWheel(MouseWheelDirection direction, + int x, + int y, + KeyboardModifiers modifiers) + { + UpdateStatusBar(direction == MouseWheelDirection_Down ? "Wheel down" : "Wheel up"); + } + + + void TestCairoWidget::KeyPressed(char key, + KeyboardModifiers modifiers) + { + UpdateStatusBar("Key pressed: \"" + std::string(1, key) + "\""); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/TestCairoWidget.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,83 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "CairoWidget.h" + +namespace OrthancStone +{ + namespace Samples + { + class TestCairoWidget : public CairoWidget + { + private: + unsigned int width_; + unsigned int height_; + float value_; + bool animate_; + + virtual bool HasUpdateThread() const + { + return animate_; + } + + virtual void UpdateStep(); + + protected: + virtual bool RenderCairo(CairoContext& context); + + virtual void RenderMouseOverCairo(CairoContext& context, + int x, + int y); + + public: + TestCairoWidget(bool animate); + + virtual void SetSize(unsigned int width, + unsigned int height); + + virtual IMouseTracker* CreateMouseTracker(MouseButton button, + int x, + int y, + KeyboardModifiers modifiers); + + virtual void MouseWheel(MouseWheelDirection direction, + int x, + int y, + KeyboardModifiers modifiers); + + virtual void KeyPressed(char key, + KeyboardModifiers modifiers); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/TestWorldSceneWidget.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,142 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "TestWorldSceneWidget.h" + +#include <stdio.h> + +namespace OrthancStone +{ + namespace Samples + { + class TestWorldSceneWidget::Interactor : public IWorldSceneInteractor + { + public: + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + MouseButton button, + double x, + double y, + IStatusBar* statusBar) + { + if (statusBar) + { + char buf[64]; + sprintf(buf, "X = %0.2f, Y = %0.2f", x, y); + statusBar->SetMessage(buf); + } + + return NULL; + } + + virtual void MouseOver(CairoContext& context, + WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) + { + double S = 0.5; + + if (fabs(x) <= S && + fabs(y) <= S) + { + cairo_t* cr = context.GetObject(); + cairo_set_source_rgb(cr, 1, 0, 0); + cairo_rectangle(cr, -S, -S , 2.0 * S, 2.0 * S); + cairo_set_line_width(cr, 1.0 / view.GetZoom()); + cairo_stroke(cr); + } + } + + virtual void MouseWheel(WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + if (statusBar) + { + statusBar->SetMessage(direction == MouseWheelDirection_Down ? "Wheel down" : "Wheel up"); + } + } + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + if (statusBar) + { + statusBar->SetMessage("Key pressed: \"" + std::string(1, key) + "\""); + } + } + }; + + + bool TestWorldSceneWidget::RenderScene(CairoContext& context, + const ViewportGeometry& view) + { + cairo_t* cr = context.GetObject(); + + // Clear background + cairo_set_source_rgb(cr, 0, 0, 0); + cairo_paint(cr); + + cairo_set_source_rgb(cr, 0, 1, 0); + cairo_rectangle(cr, -10, -.5, 20, 1); + cairo_fill(cr); + + return true; + } + + + TestWorldSceneWidget::TestWorldSceneWidget() : + interactor_(new Interactor) + { + SetInteractor(*interactor_); + } + + + void TestWorldSceneWidget::GetSceneExtent(double& x1, + double& y1, + double& x2, + double& y2) + { + x1 = -10; + x2 = 10; + y1 = -.5; + y2 = .5; + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/TestWorldSceneWidget.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,66 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "WorldSceneWidget.h" + +namespace OrthancStone +{ + namespace Samples + { + class TestWorldSceneWidget : public WorldSceneWidget + { + private: + class Interactor; + + std::auto_ptr<Interactor> interactor_; + + protected: + virtual SliceGeometry GetSlice() + { + return SliceGeometry(); + } + + virtual bool RenderScene(CairoContext& context, + const ViewportGeometry& view); + + public: + TestWorldSceneWidget(); + + virtual void GetSceneExtent(double& x1, + double& y1, + double& x2, + double& y2); + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/WidgetBase.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,190 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "WidgetBase.h" + +#include "../Orthanc/Core/OrthancException.h" +#include "../Orthanc/Core/Images/ImageProcessing.h" +#include "../Orthanc/Core/Logging.h" + +namespace OrthancStone +{ + void WidgetBase::ClearBackgroundOrthanc(Orthanc::ImageAccessor& target) const + { + // Clear the background using Orthanc + + if (backgroundCleared_) + { + Orthanc::ImageProcessing::Set(target, + backgroundColor_[0], + backgroundColor_[1], + backgroundColor_[2], + 255 /* alpha */); + } + } + + + void WidgetBase::ClearBackgroundCairo(CairoContext& context) const + { + // Clear the background using Cairo + + if (IsBackgroundCleared()) + { + uint8_t red, green, blue; + GetBackgroundColor(red, green, blue); + + context.SetSourceColor(red, green, blue); + cairo_paint(context.GetObject()); + } + } + + + void WidgetBase::ClearBackgroundCairo(Orthanc::ImageAccessor& target) const + { + CairoSurface surface(target); + CairoContext context(surface); + ClearBackgroundCairo(context); + } + + + void WidgetBase::NotifyChange() + { + observers_.NotifyChange(this); + } + + + void WidgetBase::UpdateStatusBar(const std::string& message) + { + if (statusBar_ != NULL) + { + statusBar_->SetMessage(message); + } + } + + + void WidgetBase::WorkerThread(WidgetBase* that) + { + while (that->started_) + { + that->UpdateStep(); + } + } + + + WidgetBase::WidgetBase() : + statusBar_(NULL), + started_(false), + backgroundCleared_(false) + { + backgroundColor_[0] = 0; + backgroundColor_[1] = 0; + backgroundColor_[2] = 0; + } + + + void WidgetBase::SetBackgroundColor(uint8_t red, + uint8_t green, + uint8_t blue) + { + backgroundColor_[0] = red; + backgroundColor_[1] = green; + backgroundColor_[2] = blue; + } + + void WidgetBase::GetBackgroundColor(uint8_t& red, + uint8_t& green, + uint8_t& blue) const + { + red = backgroundColor_[0]; + green = backgroundColor_[1]; + blue = backgroundColor_[2]; + } + + + void WidgetBase::Register(IChangeObserver& observer) + { + observers_.Register(observer); + } + + + void WidgetBase::Unregister(IChangeObserver& observer) + { + observers_.Unregister(observer); + } + + + void WidgetBase::Start() + { + if (started_) + { + LOG(ERROR) << "Cannot Start() twice"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + started_ = true; + + if (HasUpdateThread()) + { + thread_ = boost::thread(WorkerThread, this); + } + } + + + void WidgetBase::Stop() + { + if (!started_) + { + LOG(ERROR) << "Cannot Stop() if Start() has not been invoked"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + started_ = false; + + if (HasUpdateThread() && + thread_.joinable()) + { + thread_.join(); + } + } + + + bool WidgetBase::Render(Orthanc::ImageAccessor& surface) + { +#if 0 + ClearBackgroundOrthanc(surface); +#else + ClearBackgroundCairo(surface); // Faster than Orthanc +#endif + + return true; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/WidgetBase.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,122 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "IWidget.h" + +#include "../Viewport/CairoContext.h" +#include "../Toolbox/ObserversRegistry.h" + +#include <boost/thread.hpp> + +namespace OrthancStone +{ + class WidgetBase : public IWidget + { + private: + IStatusBar* statusBar_; + ObserversRegistry<IWidget> observers_; + bool started_; + boost::thread thread_; + bool backgroundCleared_; + uint8_t backgroundColor_[3]; + + protected: + void ClearBackgroundOrthanc(Orthanc::ImageAccessor& target) const; + + void ClearBackgroundCairo(CairoContext& context) const; + + void ClearBackgroundCairo(Orthanc::ImageAccessor& target) const; + + void NotifyChange(); + + void UpdateStatusBar(const std::string& message); + + static void WorkerThread(WidgetBase* that); + + IStatusBar* GetStatusBar() const + { + return statusBar_; + } + + virtual bool HasUpdateThread() const = 0; + + virtual void UpdateStep() = 0; + + public: + WidgetBase(); + + bool IsStarted() const + { + return started_; + } + + void SetBackgroundCleared(bool clear) + { + backgroundCleared_ = clear; + } + + bool IsBackgroundCleared() const + { + return backgroundCleared_; + } + + void SetBackgroundColor(uint8_t red, + uint8_t green, + uint8_t blue); + + void GetBackgroundColor(uint8_t& red, + uint8_t& green, + uint8_t& blue) const; + + virtual void Register(IChangeObserver& observer); + + virtual void Unregister(IChangeObserver& observer); + + virtual void SetStatusBar(IStatusBar& statusBar) + { + statusBar_ = &statusBar; + } + + virtual void ResetStatusBar() + { + statusBar_ = NULL; + } + + virtual void Start(); + + virtual void Stop(); + + virtual bool Render(Orthanc::ImageAccessor& surface); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/WorldSceneWidget.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,461 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "WorldSceneWidget.h" + +#include "../Orthanc/Core/OrthancException.h" + +namespace OrthancStone +{ + static void MapMouseToScene(double& sceneX, + double& sceneY, + const ViewportGeometry& view, + int mouseX, + int mouseY) + { + // Take the center of the pixel + double x, y; + x = static_cast<double>(mouseX) + 0.5; + y = static_cast<double>(mouseY) + 0.5; + + view.MapDisplayToScene(sceneX, sceneY, x, y); + } + + + struct WorldSceneWidget::ViewChangeFunctor + { + const ViewportGeometry& view_; + + ViewChangeFunctor(const ViewportGeometry& view) : + view_(view) + { + } + + void operator() (IWorldObserver& observer, + const WorldSceneWidget& source) + { + observer.NotifyViewChange(source, view_); + } + }; + + + struct WorldSceneWidget::SizeChangeFunctor + { + ViewportGeometry& view_; + + SizeChangeFunctor(ViewportGeometry& view) : + view_(view) + { + } + + void operator() (IWorldObserver& observer, + const WorldSceneWidget& source) + { + observer.NotifySizeChange(source, view_); + } + }; + + + class WorldSceneWidget::SceneMouseTracker : public IMouseTracker + { + private: + ViewportGeometry view_; + std::auto_ptr<IWorldSceneMouseTracker> tracker_; + + public: + SceneMouseTracker(const ViewportGeometry& view, + IWorldSceneMouseTracker* tracker) : + view_(view), + tracker_(tracker) + { + assert(tracker != NULL); + } + + virtual void Render(Orthanc::ImageAccessor& target) + { + CairoSurface surface(target); + CairoContext context(surface); + view_.ApplyTransform(context); + tracker_->Render(context, view_.GetZoom()); + } + + virtual void MouseUp() + { + tracker_->MouseUp(); + } + + virtual void MouseMove(int x, + int y) + { + double sceneX, sceneY; + MapMouseToScene(sceneX, sceneY, view_, x, y); + tracker_->MouseMove(sceneX, sceneY); + } + }; + + + class WorldSceneWidget::PanMouseTracker : public IMouseTracker + { + private: + WorldSceneWidget& that_; + double previousPanX_; + double previousPanY_; + double downX_; + double downY_; + + public: + PanMouseTracker(WorldSceneWidget& that, + int x, + int y) : + that_(that), + downX_(x), + downY_(y) + { + SharedValue<ViewportGeometry>::Locker locker(that_.view_); + locker.GetValue().GetPan(previousPanX_, previousPanY_); + } + + virtual void Render(Orthanc::ImageAccessor& surface) + { + } + + virtual void MouseUp() + { + } + + virtual void MouseMove(int x, + int y) + { + SharedValue<ViewportGeometry>::Locker locker(that_.view_); + locker.GetValue().SetPan(previousPanX_ + x - downX_, + previousPanY_ + y - downY_); + + ViewChangeFunctor functor(locker.GetValue()); + that_.observers_.Notify(&that_, functor); + } + }; + + + class WorldSceneWidget::ZoomMouseTracker : public IMouseTracker + { + private: + WorldSceneWidget& that_; + int downX_; + int downY_; + double centerX_; + double centerY_; + double oldZoom_; + + public: + ZoomMouseTracker(WorldSceneWidget& that, + int x, + int y) : + that_(that), + downX_(x), + downY_(y) + { + SharedValue<ViewportGeometry>::Locker locker(that_.view_); + oldZoom_ = locker.GetValue().GetZoom(); + MapMouseToScene(centerX_, centerY_, locker.GetValue(), downX_, downY_); + } + + virtual void Render(Orthanc::ImageAccessor& surface) + { + } + + virtual void MouseUp() + { + } + + virtual void MouseMove(int x, + int y) + { + static const double MIN_ZOOM = -4; + static const double MAX_ZOOM = 4; + + SharedValue<ViewportGeometry>::Locker locker(that_.view_); + + if (locker.GetValue().GetDisplayHeight() <= 3) + { + return; // Cannot zoom on such a small image + } + + double dy = (static_cast<double>(y - downY_) / + static_cast<double>(locker.GetValue().GetDisplayHeight() - 1)); // In the range [-1,1] + double z; + + // Linear interpolation from [-1, 1] to [MIN_ZOOM, MAX_ZOOM] + if (dy < -1.0) + { + z = MIN_ZOOM; + } + else if (dy > 1.0) + { + z = MAX_ZOOM; + } + else + { + z = MIN_ZOOM + (MAX_ZOOM - MIN_ZOOM) * (dy + 1.0) / 2.0; + } + + z = pow(2.0, z); + + locker.GetValue().SetZoom(oldZoom_ * z); + + // Correct the pan so that the original click point is kept at + // the same location on the display + double panX, panY; + locker.GetValue().GetPan(panX, panY); + + int tx, ty; + locker.GetValue().MapSceneToDisplay(tx, ty, centerX_, centerY_); + locker.GetValue().SetPan(panX + static_cast<double>(downX_ - tx), + panY + static_cast<double>(downY_ - ty)); + + ViewChangeFunctor functor(locker.GetValue()); + that_.observers_.Notify(&that_, functor); + } + }; + + + void WorldSceneWidget::UpdateStep() + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + + bool WorldSceneWidget::RenderCairo(CairoContext& context) + { + ViewportGeometry view; + + { + SharedValue<ViewportGeometry>::Locker locker(view_); + view = locker.GetValue(); + } + + view.ApplyTransform(context); + + return RenderScene(context, view); + } + + + void WorldSceneWidget::RenderMouseOverCairo(CairoContext& context, + int x, + int y) + { + ViewportGeometry view = GetView(); + view.ApplyTransform(context); + + double sceneX, sceneY; + MapMouseToScene(sceneX, sceneY, view, x, y); + RenderSceneMouseOver(context, view, sceneX, sceneY); + } + + + void WorldSceneWidget::SetSceneExtent(SharedValue<ViewportGeometry>::Locker& locker) + { + double x1, y1, x2, y2; + GetSceneExtent(x1, y1, x2, y2); + locker.GetValue().SetSceneExtent(x1, y1, x2, y2); + } + + + void WorldSceneWidget::SetSize(unsigned int width, + unsigned int height) + { + CairoWidget::SetSize(width, height); + + { + SharedValue<ViewportGeometry>::Locker locker(view_); + locker.GetValue().SetDisplaySize(width, height); + + if (observers_.IsEmpty()) + { + // Without a size observer, use the default view + locker.GetValue().SetDefaultView(); + } + else + { + // With a size observer, let it decide which view to use + SizeChangeFunctor functor(locker.GetValue()); + observers_.Notify(this, functor); + } + } + } + + + void WorldSceneWidget::SetInteractor(IWorldSceneInteractor& interactor) + { + if (IsStarted()) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadSequenceOfCalls); + } + + interactor_ = &interactor; + } + + + void WorldSceneWidget::Start() + { + ViewportGeometry geometry; + + { + SharedValue<ViewportGeometry>::Locker locker(view_); + SetSceneExtent(locker); + geometry = locker.GetValue(); + } + + WidgetBase::Start(); + + ViewChangeFunctor functor(geometry); + observers_.Notify(this, functor); + } + + + void WorldSceneWidget::SetDefaultView() + { + ViewportGeometry geometry; + + { + SharedValue<ViewportGeometry>::Locker locker(view_); + SetSceneExtent(locker); + locker.GetValue().SetDefaultView(); + geometry = locker.GetValue(); + } + + NotifyChange(); + + ViewChangeFunctor functor(geometry); + observers_.Notify(this, functor); + } + + + void WorldSceneWidget::SetView(const ViewportGeometry& view) + { + { + SharedValue<ViewportGeometry>::Locker locker(view_); + locker.GetValue() = view; + } + + NotifyChange(); + + ViewChangeFunctor functor(view); + observers_.Notify(this, functor); + } + + + ViewportGeometry WorldSceneWidget::GetView() + { + SharedValue<ViewportGeometry>::Locker locker(view_); + return locker.GetValue(); + } + + + IMouseTracker* WorldSceneWidget::CreateMouseTracker(MouseButton button, + int x, + int y, + KeyboardModifiers modifiers) + { + ViewportGeometry view = GetView(); + + double sceneX, sceneY; + MapMouseToScene(sceneX, sceneY, view, x, y); + + std::auto_ptr<IWorldSceneMouseTracker> tracker(CreateMouseSceneTracker(view, button, sceneX, sceneY, modifiers)); + if (tracker.get() != NULL) + { + return new SceneMouseTracker(view, tracker.release()); + } + + switch (button) + { + case MouseButton_Middle: + return new PanMouseTracker(*this, x, y); + + case MouseButton_Right: + return new ZoomMouseTracker(*this, x, y); + + default: + return NULL; + } + } + + + void WorldSceneWidget::RenderSceneMouseOver(CairoContext& context, + const ViewportGeometry& view, + double x, + double y) + { + if (interactor_) + { + interactor_->MouseOver(context, *this, GetSlice(), view, x, y, GetStatusBar()); + } + } + + IWorldSceneMouseTracker* WorldSceneWidget::CreateMouseSceneTracker(const ViewportGeometry& view, + MouseButton button, + double x, + double y, + KeyboardModifiers modifiers) + { + if (interactor_) + { + return interactor_->CreateMouseTracker(*this, GetSlice(), view, button, x, y, GetStatusBar()); + } + else + { + return NULL; + } + } + + + void WorldSceneWidget::MouseWheel(MouseWheelDirection direction, + int x, + int y, + KeyboardModifiers modifiers) + { + if (interactor_) + { + interactor_->MouseWheel(*this, direction, modifiers, GetStatusBar()); + } + } + + + void WorldSceneWidget::KeyPressed(char key, + KeyboardModifiers modifiers) + { + if (interactor_) + { + interactor_->KeyPressed(*this, key, modifiers, GetStatusBar()); + } + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Widgets/WorldSceneWidget.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,158 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "CairoWidget.h" +#include "IWorldSceneInteractor.h" + +#include "../Toolbox/SharedValue.h" +#include "../Toolbox/ViewportGeometry.h" + +namespace OrthancStone +{ + class WorldSceneWidget : public CairoWidget + { + public: + // Must be thread-safe + class IWorldObserver : public boost::noncopyable + { + public: + virtual ~IWorldObserver() + { + } + + virtual void NotifySizeChange(const WorldSceneWidget& source, + ViewportGeometry& view) = 0; // Can be tuned by the observer + + virtual void NotifyViewChange(const WorldSceneWidget& source, + const ViewportGeometry& view) = 0; + }; + + private: + struct ViewChangeFunctor; + struct SizeChangeFunctor; + + class SceneMouseTracker; + class PanMouseTracker; + class ZoomMouseTracker; + + typedef ObserversRegistry<WorldSceneWidget, IWorldObserver> Observers; + + SharedValue<ViewportGeometry> view_; + Observers observers_; + IWorldSceneInteractor* interactor_; + + + protected: + virtual bool RenderScene(CairoContext& context, + const ViewportGeometry& view) = 0; + + virtual bool HasUpdateThread() const + { + return false; + } + + virtual void UpdateStep(); + + virtual bool RenderCairo(CairoContext& context); + + virtual void RenderMouseOverCairo(CairoContext& context, + int x, + int y); + + void SetSceneExtent(SharedValue<ViewportGeometry>::Locker& locker); + + public: + WorldSceneWidget() : + interactor_(NULL) + { + } + + using WidgetBase::Register; + using WidgetBase::Unregister; + + void Register(IWorldObserver& observer) + { + observers_.Register(observer); + } + + void Unregister(IWorldObserver& observer) + { + observers_.Unregister(observer); + } + + virtual SliceGeometry GetSlice() = 0; + + virtual void GetSceneExtent(double& x1, + double& y1, + double& x2, + double& y2) = 0; + + virtual void SetSize(unsigned int width, + unsigned int height); + + void SetInteractor(IWorldSceneInteractor& interactor); + + virtual void Start(); + + void SetDefaultView(); + + void SetView(const ViewportGeometry& view); + + ViewportGeometry GetView(); + + virtual IMouseTracker* CreateMouseTracker(MouseButton button, + int x, + int y, + KeyboardModifiers modifiers); + + virtual void RenderSceneMouseOver(CairoContext& context, + const ViewportGeometry& view, + double x, + double y); + + virtual IWorldSceneMouseTracker* CreateMouseSceneTracker(const ViewportGeometry& view, + MouseButton button, + double x, + double y, + KeyboardModifiers modifiers); + + virtual void MouseWheel(MouseWheelDirection direction, + int x, + int y, + KeyboardModifiers modifiers); + + virtual void KeyPressed(char key, + KeyboardModifiers modifiers); + }; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/NEWS Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,4 @@ +2016-10-14 +========== + +* Initial release
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/README Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,102 @@ +Stone of Orthanc +================ + + +General Information +------------------- + +This repository contains the source code of the Stone of Orthanc. + +Stone of Orthanc is a lightweight, cross-platform C++ framework for +the CPU-based rendering of medical images. It notably features support +for MPR (multiplanar reconstruction of volume images), PET-CT fusion, +and accurate physical world coordinates. + +Stone of Orthanc comes bundled with its own software-based rendering +engine (based upon pixman). This engine will use CPU hardware +acceleration if available (notably SSE2, SSSE3, and NEON instruction +sets), but not the GPU. This makes Stone a highly versatile framework +that can run even on low-performance platforms. Stone is able to +display DICOM series without having to entirely store them in the RAM. + +Stone of Orthanc is conceived as a companion toolbox to the Orthanc +VNA (vendor neutral archive, i.e. DICOM server). As a consequence, +Stone will smoothly interface with Orthanc out of the +box. Interestingly, Stone does not contain any DICOM toolkit: It +entirely relies on the REST API of Orthanc to parse/decode DICOM +images. However, thanks to the object-oriented architecture of Stone, +it is possible to avoid this dependency upon Orthanc. + + +Comparison +---------- + +Pay attention to the fact that Stone of Orthanc is a toolkit, and not +a fully-featured application for the visualization of medical +images. Such applications can be built on the top of Stone of Orthanc. + +Stone of Orthanc is quite similar to two other well-known toolkits: + +* Cornerstone, a client-side JavaScript toolkit to display medical + images in Web browsers, by Chris Hafey <chafey@gmail.com>: + https://github.com/chafey/cornerstone + + Contrarily to Cornerstone, Stone of Orthanc can be embedded into + native, heavyweight applications. + +* VTK, a C++ toolkit for scientific visualization, by Kitware: + http://www.vtk.org/ + + Contrarily to VTK, Stone of Orthanc is focused on CPU-based, 2D + rendering: The GPU will not be used. + + +Dependencies +------------ + +Stone of Orthanc is based upon the following projects: + +* Orthanc, a lightweight Vendor Neutral Archive (DICOM server): + http://www.orthanc-server.com/ + +* Cairo and pixman, a cross-platform 2D graphics library: + https://www.cairographics.org/ + +* Optionally, SDL, a cross-platform multimedia library: + https://www.libsdl.org/ + + +Installation and usage +---------------------- + +Build instructions are similar to that of Orthanc: +https://orthanc.chu.ulg.ac.be/book/faq/compiling.html + +Stone of Orthanc comes with several sample applications in the +"Samples" folder. These samples use SDL. + + +Licensing +--------- + +Stone of Orthanc is licensed under the GPLv3 license, with the OpenSSL +exception: +http://people.gnome.org/~markmc/openssl-and-the-gpl.html + +We also kindly ask scientific works and clinical studies that make +use of Orthanc to cite Orthanc in their associated publications. +Similarly, we ask open-source and closed-source products that make +use of Orthanc to warn us about this use. You can cite our work +using the following BibTeX entry: + +@inproceedings{Jodogne:ISBI2013, + author = {Jodogne, S. and Bernard, C. and Devillers, M. and Lenaerts, E. and Coucke, P.}, + title = {Orthanc -- {A} Lightweight, {REST}ful {DICOM} Server for Healthcare and Medical Research}, + booktitle={Biomedical Imaging ({ISBI}), {IEEE} 10th International Symposium on}, + year={2013}, + pages={190-193}, + ISSN={1945-7928}, + month=apr, + url={http://ieeexplore.ieee.org/xpl/articleDetails.jsp?tp=&arnumber=6556444}, + address={San Francisco, {CA}, {USA}} +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/CMake/BoostExtendedConfiguration.cmake Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,21 @@ +SET(ORTHANC_BOOST_COMPONENTS program_options) + +include(${ORTHANC_ROOT}/Resources/CMake/BoostConfiguration.cmake) + +if (BOOST_STATIC) + list(APPEND BOOST_SOURCES + ${BOOST_SOURCES_DIR}/libs/program_options/src/cmdline.cpp + ${BOOST_SOURCES_DIR}/libs/program_options/src/config_file.cpp + ${BOOST_SOURCES_DIR}/libs/program_options/src/convert.cpp + ${BOOST_SOURCES_DIR}/libs/program_options/src/options_description.cpp + ${BOOST_SOURCES_DIR}/libs/program_options/src/parsers.cpp + ${BOOST_SOURCES_DIR}/libs/program_options/src/positional_options.cpp + ${BOOST_SOURCES_DIR}/libs/program_options/src/split.cpp + ${BOOST_SOURCES_DIR}/libs/program_options/src/utf8_codecvt_facet.cpp + ${BOOST_SOURCES_DIR}/libs/program_options/src/value_semantic.cpp + ${BOOST_SOURCES_DIR}/libs/program_options/src/variables_map.cpp + #${BOOST_SOURCES_DIR}/libs/program_options/src/winmain.cpp + ) + add_definitions(-DBOOST_PROGRAM_OPTIONS_NO_LIB) +endif() +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/CMake/CairoConfiguration.cmake Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,223 @@ +# ./configure --disable-pdf --disable-svg --disable-xlib --disable-xcb --disable-script --disable-ps --disable-ft --disable-fc --disable-png --disable-trace --disable-interpreter + + +if (STATIC_BUILD OR NOT USE_SYSTEM_CAIRO) + SET(CAIRO_SOURCES_DIR ${CMAKE_BINARY_DIR}/cairo-1.14.6) + SET(CAIRO_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/Stone/cairo-1.14.6.tar.xz") + SET(CAIRO_MD5 "23a0b2f0235431d35238df1d3a517fdb") + + DownloadPackage(${CAIRO_MD5} ${CAIRO_URL} "${CAIRO_SOURCES_DIR}") + + file(COPY + ${CMAKE_CURRENT_LIST_DIR}/cairo-features.h + DESTINATION ${CAIRO_SOURCES_DIR}/src + ) + + set(CAIRO_SOURCES + ${CAIRO_SOURCES_DIR}/src/cairo-analysis-surface.c + ${CAIRO_SOURCES_DIR}/src/cairo-arc.c + ${CAIRO_SOURCES_DIR}/src/cairo-array.c + ${CAIRO_SOURCES_DIR}/src/cairo-atomic.c + ${CAIRO_SOURCES_DIR}/src/cairo-base64-stream.c + ${CAIRO_SOURCES_DIR}/src/cairo-base85-stream.c + ${CAIRO_SOURCES_DIR}/src/cairo-bentley-ottmann.c + ${CAIRO_SOURCES_DIR}/src/cairo-bentley-ottmann-rectangular.c + ${CAIRO_SOURCES_DIR}/src/cairo-bentley-ottmann-rectilinear.c + ${CAIRO_SOURCES_DIR}/src/cairo-botor-scan-converter.c + ${CAIRO_SOURCES_DIR}/src/cairo-boxes.c + ${CAIRO_SOURCES_DIR}/src/cairo-boxes-intersect.c + ${CAIRO_SOURCES_DIR}/src/cairo.c + ${CAIRO_SOURCES_DIR}/src/cairo-cache.c + ${CAIRO_SOURCES_DIR}/src/cairo-cff-subset.c + ${CAIRO_SOURCES_DIR}/src/cairo-clip-boxes.c + ${CAIRO_SOURCES_DIR}/src/cairo-clip.c + ${CAIRO_SOURCES_DIR}/src/cairo-clip-polygon.c + ${CAIRO_SOURCES_DIR}/src/cairo-clip-region.c + ${CAIRO_SOURCES_DIR}/src/cairo-clip-surface.c + ${CAIRO_SOURCES_DIR}/src/cairo-clip-tor-scan-converter.c + # ${CAIRO_SOURCES_DIR}/src/cairo-cogl-context.c + # ${CAIRO_SOURCES_DIR}/src/cairo-cogl-gradient.c + # ${CAIRO_SOURCES_DIR}/src/cairo-cogl-surface.c + # ${CAIRO_SOURCES_DIR}/src/cairo-cogl-utils.c + ${CAIRO_SOURCES_DIR}/src/cairo-color.c + ${CAIRO_SOURCES_DIR}/src/cairo-composite-rectangles.c + ${CAIRO_SOURCES_DIR}/src/cairo-compositor.c + ${CAIRO_SOURCES_DIR}/src/cairo-contour.c + ${CAIRO_SOURCES_DIR}/src/cairo-damage.c + ${CAIRO_SOURCES_DIR}/src/cairo-debug.c + ${CAIRO_SOURCES_DIR}/src/cairo-default-context.c + ${CAIRO_SOURCES_DIR}/src/cairo-deflate-stream.c + ${CAIRO_SOURCES_DIR}/src/cairo-device.c + # ${CAIRO_SOURCES_DIR}/src/cairo-directfb-surface.c + # ${CAIRO_SOURCES_DIR}/src/cairo-egl-context.c + ${CAIRO_SOURCES_DIR}/src/cairo-error.c + ${CAIRO_SOURCES_DIR}/src/cairo-fallback-compositor.c + ${CAIRO_SOURCES_DIR}/src/cairo-fixed.c + ${CAIRO_SOURCES_DIR}/src/cairo-font-face.c + ${CAIRO_SOURCES_DIR}/src/cairo-font-face-twin.c + ${CAIRO_SOURCES_DIR}/src/cairo-font-face-twin-data.c + ${CAIRO_SOURCES_DIR}/src/cairo-font-options.c + ${CAIRO_SOURCES_DIR}/src/cairo-freed-pool.c + ${CAIRO_SOURCES_DIR}/src/cairo-freelist.c + # ${CAIRO_SOURCES_DIR}/src/cairo-ft-font.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-composite.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-device.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-dispatch.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-glyphs.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-gradient.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-info.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-msaa-compositor.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-operand.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-shaders.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-source.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-spans-compositor.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-surface.c + # ${CAIRO_SOURCES_DIR}/src/cairo-gl-traps-compositor.c + # ${CAIRO_SOURCES_DIR}/src/cairo-glx-context.c + ${CAIRO_SOURCES_DIR}/src/cairo-gstate.c + ${CAIRO_SOURCES_DIR}/src/cairo-hash.c + ${CAIRO_SOURCES_DIR}/src/cairo-hull.c + ${CAIRO_SOURCES_DIR}/src/cairo-image-compositor.c + ${CAIRO_SOURCES_DIR}/src/cairo-image-info.c + ${CAIRO_SOURCES_DIR}/src/cairo-image-source.c + ${CAIRO_SOURCES_DIR}/src/cairo-image-surface.c + ${CAIRO_SOURCES_DIR}/src/cairo-line.c + ${CAIRO_SOURCES_DIR}/src/cairo-lzw.c + ${CAIRO_SOURCES_DIR}/src/cairo-mask-compositor.c + ${CAIRO_SOURCES_DIR}/src/cairo-matrix.c + ${CAIRO_SOURCES_DIR}/src/cairo-mempool.c + ${CAIRO_SOURCES_DIR}/src/cairo-mesh-pattern-rasterizer.c + ${CAIRO_SOURCES_DIR}/src/cairo-misc.c + ${CAIRO_SOURCES_DIR}/src/cairo-mono-scan-converter.c + ${CAIRO_SOURCES_DIR}/src/cairo-mutex.c + ${CAIRO_SOURCES_DIR}/src/cairo-no-compositor.c + ${CAIRO_SOURCES_DIR}/src/cairo-observer.c + # ${CAIRO_SOURCES_DIR}/src/cairo-os2-surface.c + ${CAIRO_SOURCES_DIR}/src/cairo-output-stream.c + ${CAIRO_SOURCES_DIR}/src/cairo-paginated-surface.c + ${CAIRO_SOURCES_DIR}/src/cairo-path-bounds.c + ${CAIRO_SOURCES_DIR}/src/cairo-path.c + ${CAIRO_SOURCES_DIR}/src/cairo-path-fill.c + ${CAIRO_SOURCES_DIR}/src/cairo-path-fixed.c + ${CAIRO_SOURCES_DIR}/src/cairo-path-in-fill.c + ${CAIRO_SOURCES_DIR}/src/cairo-path-stroke-boxes.c + ${CAIRO_SOURCES_DIR}/src/cairo-path-stroke.c + ${CAIRO_SOURCES_DIR}/src/cairo-path-stroke-polygon.c + ${CAIRO_SOURCES_DIR}/src/cairo-path-stroke-traps.c + ${CAIRO_SOURCES_DIR}/src/cairo-path-stroke-tristrip.c + ${CAIRO_SOURCES_DIR}/src/cairo-pattern.c + # ${CAIRO_SOURCES_DIR}/src/cairo-pdf-operators.c + # ${CAIRO_SOURCES_DIR}/src/cairo-pdf-shading.c + # ${CAIRO_SOURCES_DIR}/src/cairo-pdf-surface.c + ${CAIRO_SOURCES_DIR}/src/cairo-pen.c + # ${CAIRO_SOURCES_DIR}/src/cairo-png.c + ${CAIRO_SOURCES_DIR}/src/cairo-polygon.c + ${CAIRO_SOURCES_DIR}/src/cairo-polygon-intersect.c + ${CAIRO_SOURCES_DIR}/src/cairo-polygon-reduce.c + # ${CAIRO_SOURCES_DIR}/src/cairo-ps-surface.c + # ${CAIRO_SOURCES_DIR}/src/cairo-quartz-font.c + # ${CAIRO_SOURCES_DIR}/src/cairo-quartz-image-surface.c + # ${CAIRO_SOURCES_DIR}/src/cairo-quartz-surface.c + ${CAIRO_SOURCES_DIR}/src/cairo-raster-source-pattern.c + ${CAIRO_SOURCES_DIR}/src/cairo-recording-surface.c + ${CAIRO_SOURCES_DIR}/src/cairo-rectangle.c + ${CAIRO_SOURCES_DIR}/src/cairo-rectangular-scan-converter.c + ${CAIRO_SOURCES_DIR}/src/cairo-region.c + ${CAIRO_SOURCES_DIR}/src/cairo-rtree.c + ${CAIRO_SOURCES_DIR}/src/cairo-scaled-font.c + ${CAIRO_SOURCES_DIR}/src/cairo-scaled-font-subsets.c + # ${CAIRO_SOURCES_DIR}/src/cairo-script-surface.c + ${CAIRO_SOURCES_DIR}/src/cairo-shape-mask-compositor.c + ${CAIRO_SOURCES_DIR}/src/cairo-slope.c + ${CAIRO_SOURCES_DIR}/src/cairo-spans.c + ${CAIRO_SOURCES_DIR}/src/cairo-spans-compositor.c + ${CAIRO_SOURCES_DIR}/src/cairo-spline.c + ${CAIRO_SOURCES_DIR}/src/cairo-stroke-dash.c + ${CAIRO_SOURCES_DIR}/src/cairo-stroke-style.c + ${CAIRO_SOURCES_DIR}/src/cairo-surface.c + ${CAIRO_SOURCES_DIR}/src/cairo-surface-clipper.c + ${CAIRO_SOURCES_DIR}/src/cairo-surface-fallback.c + ${CAIRO_SOURCES_DIR}/src/cairo-surface-observer.c + ${CAIRO_SOURCES_DIR}/src/cairo-surface-offset.c + ${CAIRO_SOURCES_DIR}/src/cairo-surface-snapshot.c + ${CAIRO_SOURCES_DIR}/src/cairo-surface-subsurface.c + ${CAIRO_SOURCES_DIR}/src/cairo-surface-wrapper.c + # ${CAIRO_SOURCES_DIR}/src/cairo-svg-surface.c + # ${CAIRO_SOURCES_DIR}/src/cairo-tee-surface.c + ${CAIRO_SOURCES_DIR}/src/cairo-time.c + ${CAIRO_SOURCES_DIR}/src/cairo-tor22-scan-converter.c + ${CAIRO_SOURCES_DIR}/src/cairo-tor-scan-converter.c + ${CAIRO_SOURCES_DIR}/src/cairo-toy-font-face.c + ${CAIRO_SOURCES_DIR}/src/cairo-traps.c + ${CAIRO_SOURCES_DIR}/src/cairo-traps-compositor.c + ${CAIRO_SOURCES_DIR}/src/cairo-tristrip.c + ${CAIRO_SOURCES_DIR}/src/cairo-truetype-subset.c + ${CAIRO_SOURCES_DIR}/src/cairo-type1-fallback.c + ${CAIRO_SOURCES_DIR}/src/cairo-type1-glyph-names.c + ${CAIRO_SOURCES_DIR}/src/cairo-type1-subset.c + ${CAIRO_SOURCES_DIR}/src/cairo-type3-glyph-surface.c + ${CAIRO_SOURCES_DIR}/src/cairo-unicode.c + ${CAIRO_SOURCES_DIR}/src/cairo-user-font.c + ${CAIRO_SOURCES_DIR}/src/cairo-version.c + # ${CAIRO_SOURCES_DIR}/src/cairo-vg-surface.c + # ${CAIRO_SOURCES_DIR}/src/cairo-wgl-context.c + ${CAIRO_SOURCES_DIR}/src/cairo-wideint.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xcb-connection.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xcb-connection-core.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xcb-connection-render.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xcb-connection-shm.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xcb-resources.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xcb-screen.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xcb-shm.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xcb-surface.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xcb-surface-core.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xcb-surface-render.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xlib-core-compositor.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xlib-display.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xlib-fallback-compositor.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xlib-render-compositor.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xlib-screen.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xlib-source.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xlib-surface.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xlib-surface-shm.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xlib-visual.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xlib-xcb-surface.c + # ${CAIRO_SOURCES_DIR}/src/cairo-xml-surface.c + ) + + include_directories(${CAIRO_SOURCES_DIR}/src) + + set(CAIRO_DEFINITIONS "HAS_PIXMAN_GLYPHS=1") + + if (CMAKE_COMPILER_IS_GNUCXX) + set(CAIRO_DEFINITIONS "${CAIRO_DEFINITIONS};HAVE_STDINT_H=1;CAIRO_HAS_PTHREAD=1;HAVE_UINT64_T=1") + + if (${CMAKE_SYSTEM_NAME} STREQUAL "PNaCl") + # Disable vectorized instructions when targeting archicture-independent PNaCl + else() + set(CAIRO_DEFINITIONS "${CAIRO_DEFINITIONS};CAIRO_HAS_REAL_PTHREAD=1;HAVE_GCC_VECTOR_EXTENSIONS;HAVE_FLOAT128") + endif() + + set_property( + SOURCE ${CAIRO_SOURCES} + PROPERTY COMPILE_FLAGS "-Wno-attributes" + ) + + elseif (MSVC) + + else() + message(FATAL_ERROR "Support your platform here") + + endif() + + set_property( + SOURCE ${CAIRO_SOURCES} + PROPERTY COMPILE_DEFINITIONS "${CAIRO_DEFINITIONS}" + ) + +else() + + pkg_search_module(CAIRO REQUIRED cairo) + include_directories(${CAIRO_INCLUDE_DIRS}) + link_libraries(${CAIRO_LIBRARIES}) +endif()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/CMake/OrthancStone.cmake Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,208 @@ +##################################################################### +## Parameters of the build +##################################################################### + +# Generic parameters +SET(STATIC_BUILD OFF CACHE BOOL "Static build of the third-party libraries (necessary for Windows)") +SET(ALLOW_DOWNLOADS OFF CACHE BOOL "Allow CMake to download packages") + +# Optional components +SET(ENABLE_CURL ON CACHE BOOL "Include support for libcurl") +SET(ENABLE_SSL OFF CACHE BOOL "Include support for SSL") +SET(ENABLE_SDL ON CACHE BOOL "Include support for SDL") +SET(ENABLE_LOGGING ON CACHE BOOL "Enable logging facilities from Orthanc") + +# Advanced parameters to fine-tune linking against system libraries +SET(USE_SYSTEM_BOOST ON CACHE BOOL "Use the system version of Boost") +SET(USE_SYSTEM_JSONCPP ON CACHE BOOL "Use the system version of JsonCpp") +SET(USE_SYSTEM_ZLIB ON CACHE BOOL "Use the system version of ZLib") +SET(USE_SYSTEM_CAIRO ON CACHE BOOL "Use the system version of Cairo") +SET(USE_SYSTEM_PIXMAN ON CACHE BOOL "Use the system version of Pixman") +SET(USE_SYSTEM_LIBPNG ON CACHE BOOL "Use the system version of libpng") +SET(USE_SYSTEM_LIBJPEG ON CACHE BOOL "Use the system version of libjpeg") +SET(USE_SYSTEM_CURL ON CACHE BOOL "Use the system version of LibCurl") +SET(USE_SYSTEM_OPENSSL ON CACHE BOOL "Use the system version of OpenSSL") +SET(USE_SYSTEM_SDL ON CACHE BOOL "Use the system version of SDL2") + + +##################################################################### +## Configure mandatory third-party components +##################################################################### + +SET(ORTHANC_STONE_DIR ${CMAKE_CURRENT_LIST_DIR}/../..) +SET(ORTHANC_ROOT ${ORTHANC_STONE_DIR}/Framework/Orthanc) + +include(CheckIncludeFiles) +include(CheckIncludeFileCXX) +include(CheckLibraryExists) +include(FindPythonInterp) +include(FindPkgConfig) + +include(${ORTHANC_ROOT}/Resources/CMake/Compiler.cmake) +include(${ORTHANC_ROOT}/Resources/CMake/AutoGeneratedCode.cmake) +include(${ORTHANC_ROOT}/Resources/CMake/DownloadPackage.cmake) + +include(${ORTHANC_ROOT}/Resources/CMake/JsonCppConfiguration.cmake) +include(${ORTHANC_ROOT}/Resources/CMake/ZlibConfiguration.cmake) +include(${ORTHANC_ROOT}/Resources/CMake/LibPngConfiguration.cmake) +include(${ORTHANC_ROOT}/Resources/CMake/LibJpegConfiguration.cmake) + +include(${CMAKE_CURRENT_LIST_DIR}/BoostExtendedConfiguration.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/CairoConfiguration.cmake) +include(${CMAKE_CURRENT_LIST_DIR}/PixmanConfiguration.cmake) + + +##################################################################### +## Configure optional third-party components +##################################################################### + +if (ENABLE_LOGGING) + add_definitions(-DORTHANC_ENABLE_LOGGING=1) +else() + add_definitions(-DORTHANC_ENABLE_LOGGING=0) +endif() + +if (ENABLE_SDL) + include(${CMAKE_CURRENT_LIST_DIR}/SdlConfiguration.cmake) + add_definitions(-DORTHANC_ENABLE_SDL=1) +else() + add_definitions(-DORTHANC_ENABLE_SDL=0) +endif() + +if (ENABLE_CURL) + add_definitions(-DORTHANC_ENABLE_CURL=1) + include(${ORTHANC_ROOT}/Resources/CMake/LibCurlConfiguration.cmake) + + if (ENABLE_SSL) + set(ENABLE_PKCS11 OFF) + add_definitions(-DORTHANC_SSL_ENABLED=1) + include(${ORTHANC_ROOT}/Resources/CMake/OpenSslConfiguration.cmake) + else() + add_definitions(-DORTHANC_SSL_ENABLED=0) + endif() +else() + add_definitions( + -DORTHANC_SSL_ENABLED=0 + -DORTHANC_ENABLE_CURL=0 + ) +endif() + +add_definitions( + -DORTHANC_ENABLE_MD5=0 + -DORTHANC_ENABLE_BASE64=1 + -DORTHANC_PUGIXML_ENABLED=0 + -DORTHANC_PKCS11_ENABLED=0 + ) + + +##################################################################### +## Link the colormaps into the binaries +##################################################################### + +EmbedResources( + COLORMAP_HOT ${ORTHANC_STONE_DIR}/Resources/Colormaps/hot.lut + COLORMAP_JET ${ORTHANC_STONE_DIR}/Resources/Colormaps/jet.lut + COLORMAP_RED ${ORTHANC_STONE_DIR}/Resources/Colormaps/red.lut + COLORMAP_GREEN ${ORTHANC_STONE_DIR}/Resources/Colormaps/green.lut + COLORMAP_BLUE ${ORTHANC_STONE_DIR}/Resources/Colormaps/blue.lut + ) + + +##################################################################### +## System-specific patches +##################################################################### + +if (${CMAKE_SYSTEM_NAME} STREQUAL "Windows" AND + NOT MSVC AND + ENABLE_SDL) + # This is necessary when compiling EXE for Windows using MinGW + link_libraries(mingw32) +endif() + + + +##################################################################### +## All the source files required to build Stone of Orthanc +##################################################################### + +list(APPEND ORTHANC_STONE_SOURCES + ${ORTHANC_STONE_DIR}/Framework/Applications/BasicApplicationContext.cpp + ${ORTHANC_STONE_DIR}/Framework/Applications/IBasicApplication.cpp + ${ORTHANC_STONE_DIR}/Framework/Applications/Sdl/SdlBuffering.cpp + ${ORTHANC_STONE_DIR}/Framework/Applications/Sdl/SdlEngine.cpp + ${ORTHANC_STONE_DIR}/Framework/Applications/Sdl/SdlWindow.cpp + ${ORTHANC_STONE_DIR}/Framework/Layers/CircleMeasureTracker.cpp + ${ORTHANC_STONE_DIR}/Framework/Layers/ColorFrameRenderer.cpp + ${ORTHANC_STONE_DIR}/Framework/Layers/DicomStructureSetRendererFactory.cpp + ${ORTHANC_STONE_DIR}/Framework/Layers/FrameRenderer.cpp + ${ORTHANC_STONE_DIR}/Framework/Layers/GrayscaleFrameRenderer.cpp + ${ORTHANC_STONE_DIR}/Framework/Layers/LineLayerRenderer.cpp + ${ORTHANC_STONE_DIR}/Framework/Layers/LineMeasureTracker.cpp + ${ORTHANC_STONE_DIR}/Framework/Layers/RenderStyle.cpp + ${ORTHANC_STONE_DIR}/Framework/Layers/SeriesFrameRendererFactory.cpp + ${ORTHANC_STONE_DIR}/Framework/Layers/SiblingSliceLocationFactory.cpp + ${ORTHANC_STONE_DIR}/Framework/Layers/SingleFrameRendererFactory.cpp + ${ORTHANC_STONE_DIR}/Framework/Messaging/CurlOrthancConnection.cpp + ${ORTHANC_STONE_DIR}/Framework/Messaging/MessagingToolbox.cpp + ${ORTHANC_STONE_DIR}/Framework/Toolbox/BinarySemaphore.cpp + ${ORTHANC_STONE_DIR}/Framework/Toolbox/DicomDataset.cpp + ${ORTHANC_STONE_DIR}/Framework/Toolbox/DicomFrameConverter.cpp + ${ORTHANC_STONE_DIR}/Framework/Toolbox/DicomStructureSet.cpp + ${ORTHANC_STONE_DIR}/Framework/Toolbox/DownloadStack.cpp + ${ORTHANC_STONE_DIR}/Framework/Toolbox/GeometryToolbox.cpp + ${ORTHANC_STONE_DIR}/Framework/Toolbox/OrthancSeriesLoader.cpp + ${ORTHANC_STONE_DIR}/Framework/Toolbox/ParallelSlices.cpp + ${ORTHANC_STONE_DIR}/Framework/Toolbox/ParallelSlicesCursor.cpp + ${ORTHANC_STONE_DIR}/Framework/Toolbox/SliceGeometry.cpp + ${ORTHANC_STONE_DIR}/Framework/Toolbox/ViewportGeometry.cpp + ${ORTHANC_STONE_DIR}/Framework/Viewport/CairoContext.cpp + ${ORTHANC_STONE_DIR}/Framework/Viewport/CairoFont.cpp + ${ORTHANC_STONE_DIR}/Framework/Viewport/CairoSurface.cpp + ${ORTHANC_STONE_DIR}/Framework/Viewport/WidgetViewport.cpp + ${ORTHANC_STONE_DIR}/Framework/Volumes/ImageBuffer3D.cpp + ${ORTHANC_STONE_DIR}/Framework/Volumes/VolumeImage.cpp + ${ORTHANC_STONE_DIR}/Framework/Volumes/VolumeImagePolicyBase.cpp + ${ORTHANC_STONE_DIR}/Framework/Volumes/VolumeImageProgressivePolicy.cpp + ${ORTHANC_STONE_DIR}/Framework/Volumes/VolumeImageSimplePolicy.cpp + ${ORTHANC_STONE_DIR}/Framework/Widgets/CairoWidget.cpp + ${ORTHANC_STONE_DIR}/Framework/Widgets/EmptyWidget.cpp + ${ORTHANC_STONE_DIR}/Framework/Widgets/LayeredSceneWidget.cpp + ${ORTHANC_STONE_DIR}/Framework/Widgets/LayoutWidget.cpp + ${ORTHANC_STONE_DIR}/Framework/Widgets/TestCairoWidget.cpp + ${ORTHANC_STONE_DIR}/Framework/Widgets/TestWorldSceneWidget.cpp + ${ORTHANC_STONE_DIR}/Framework/Widgets/WidgetBase.cpp + ${ORTHANC_STONE_DIR}/Framework/Widgets/WorldSceneWidget.cpp + + ${ORTHANC_ROOT}/Core/ChunkedBuffer.cpp + ${ORTHANC_ROOT}/Core/Compression/DeflateBaseCompressor.cpp + ${ORTHANC_ROOT}/Core/Compression/GzipCompressor.cpp + ${ORTHANC_ROOT}/Core/Enumerations.cpp + ${ORTHANC_ROOT}/Core/HttpClient.cpp + ${ORTHANC_ROOT}/Core/Images/Image.cpp + ${ORTHANC_ROOT}/Core/Images/ImageAccessor.cpp + ${ORTHANC_ROOT}/Core/Images/ImageBuffer.cpp + ${ORTHANC_ROOT}/Core/Images/ImageProcessing.cpp + ${ORTHANC_ROOT}/Core/Images/JpegErrorManager.cpp + ${ORTHANC_ROOT}/Core/Images/JpegReader.cpp + ${ORTHANC_ROOT}/Core/Images/PngReader.cpp + ${ORTHANC_ROOT}/Core/Logging.cpp + ${ORTHANC_ROOT}/Core/Toolbox.cpp + ${ORTHANC_ROOT}/Core/WebServiceParameters.cpp + ${ORTHANC_ROOT}/Resources/ThirdParty/base64/base64.cpp + + ${AUTOGENERATED_SOURCES} + + # Mandatory components + ${BOOST_SOURCES} + ${CAIRO_SOURCES} + ${JSONCPP_SOURCES} + ${PIXMAN_SOURCES} + ${ZLIB_SOURCES} + ${LIBPNG_SOURCES} + ${LIBJPEG_SOURCES} + + # Optional components + ${OPENSSL_SOURCES} + ${CURL_SOURCES} + ${SDL_SOURCES} + )
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/CMake/PixmanConfiguration.cmake Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,206 @@ +if (STATIC_BUILD OR NOT USE_SYSTEM_PIXMAN) + SET(PIXMAN_SOURCES_DIR ${CMAKE_BINARY_DIR}/pixman-0.34.0) + SET(PIXMAN_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/Stone/pixman-0.34.0.tar.gz") + SET(PIXMAN_MD5 "e80ebae4da01e77f68744319f01d52a3") + + if (IS_DIRECTORY "${PIXMAN_SOURCES_DIR}") + set(FirstRun OFF) + else() + set(FirstRun ON) + endif() + + DownloadPackage(${PIXMAN_MD5} ${PIXMAN_URL} "${PIXMAN_SOURCES_DIR}") + + # Apply a patch for NaCl32: This bypasses the custom implementation of + # "cpuid" that makes use of assembly code leading to "unrecognized + # instruction" when validating ".nexe" files using "ncval" + execute_process( + COMMAND ${PATCH_EXECUTABLE} -p0 -N -i ${CMAKE_CURRENT_LIST_DIR}/PixmanConfiguration.patch + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + RESULT_VARIABLE Failure + ) + + if (Failure AND FirstRun) + message(FATAL_ERROR "Error while patching a file") + endif() + + set(PIXMAN_VERSION_MAJOR 0) + set(PIXMAN_VERSION_MINOR 34) + set(PIXMAN_VERSION_MICRO 0) + configure_file( + ${PIXMAN_SOURCES_DIR}/pixman/pixman-version.h.in + ${PIXMAN_SOURCES_DIR}/pixman/pixman-version.h) + + list(APPEND PIXMAN_SOURCES + ${PIXMAN_SOURCES_DIR}/pixman/pixman-access-accessors.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-access.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-arm.c + #${PIXMAN_SOURCES_DIR}/pixman/pixman-arm-neon.c + #${PIXMAN_SOURCES_DIR}/pixman/pixman-arm-simd.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-bits-image.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-combine32.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-combine-float.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-conical-gradient.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-edge-accessors.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-edge.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-fast-path.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-filter.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-general.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-glyph.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-gradient-walker.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-image.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-implementation.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-linear-gradient.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-matrix.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-mips.c + #${PIXMAN_SOURCES_DIR}/pixman/pixman-mips-dspr2.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-mmx.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-noop.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-ppc.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-radial-gradient.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-region16.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-region32.c + #${PIXMAN_SOURCES_DIR}/pixman/pixman-region.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-solid-fill.c + #${PIXMAN_SOURCES_DIR}/pixman/pixman-sse2.c + #${PIXMAN_SOURCES_DIR}/pixman/pixman-ssse3.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-timer.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-trap.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-utils.c + #${PIXMAN_SOURCES_DIR}/pixman/pixman-vmx.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-x86.c + ) + + set(PIXMAN_DEFINITIONS "PACKAGE=\"pixman\"") + + if (CMAKE_SYSTEM_PROCESSOR) + message("Processor: ${CMAKE_SYSTEM_PROCESSOR}") + else() + message("Processor: Not applicable") + endif() + + + if (CMAKE_SYSTEM_NAME STREQUAL "Windows") + + ########################## + ## Windows 32 or 64 + ########################## + + if (CMAKE_COMPILER_IS_GNUCXX) + set(PIXMAN_DEFINITIONS "${PIXMAN_DEFINITIONS};TLS=__thread;HAVE_GCC_VECTOR_EXTENSIONS;HAVE_BUILTIN_CLZ;HAVE_FEDIVBYZERO=1;HAVE_FENV_H=1;HAVE_MPROTECT=1;HAVE_FLOAT128;HAVE_POSIX_MEMALIGN;USE_GCC_INLINE_ASM=1;HAVE_GETPAGESIZE=1") + + # The option "-mstackrealign" is necessary to avoid a crash on + # Windows if enabling SSE2. As an alternative, it is possible to + # fully disable hardware acceleration. + # https://bugs.freedesktop.org/show_bug.cgi?id=68300#c4 + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2 -mssse3 -mstackrealign") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2 -mssse3 -mstackrealign") + endif() + + list(APPEND PIXMAN_SOURCES + ${PIXMAN_SOURCES_DIR}/pixman/pixman-sse2.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-ssse3.c + ) + add_definitions( + -DUSE_X86_MMX=1 + -DUSE_SSE2=1 + -DUSE_SSSE3=1 + ) + + + ########################## + ## Generic x86 processor + ########################## + + elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "x86" OR + CMAKE_SYSTEM_PROCESSOR STREQUAL "x86_64" OR + CMAKE_SYSTEM_NAME STREQUAL "NaCl32" OR + CMAKE_SYSTEM_NAME STREQUAL "NaCl64") + + set(PIXMAN_DEFINITIONS "${PIXMAN_DEFINITIONS};TLS=__thread;HAVE_GCC_VECTOR_EXTENSIONS;HAVE_BUILTIN_CLZ;HAVE_MPROTECT=1;HAVE_FLOAT128;HAVE_POSIX_MEMALIGN;USE_GCC_INLINE_ASM;HAVE_GETPAGESIZE=1") + + if (${CMAKE_SYSTEM_NAME} STREQUAL "NaCl32" OR + ${CMAKE_SYSTEM_NAME} STREQUAL "NaCl64") + # The MMX instructions lead to "unrecognized instruction" when + # validating ".nexe" files using "ncval", disable them + else() + #add_definitions(-DUSE_X86_MMX=1) + endif() + + list(APPEND PIXMAN_SOURCES + ${PIXMAN_SOURCES_DIR}/pixman/pixman-sse2.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-ssse3.c + ) + add_definitions( + -DUSE_SSE2=1 + -DUSE_SSSE3=1 + ) + SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2 -mssse3") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2 -mssse3") + + + ########################## + ## ARM processor + ########################## + + elseif (CMAKE_SYSTEM_PROCESSOR STREQUAL "armv5te" OR + CMAKE_SYSTEM_PROCESSOR STREQUAL "armv6" OR + CMAKE_SYSTEM_PROCESSOR STREQUAL "armv7-a" OR + CMAKE_SYSTEM_PROCESSOR STREQUAL "aarch64") + + set(PIXMAN_DEFINITIONS "${PIXMAN_DEFINITIONS};TLS=__thread") + + if (NEON) + message("Processor with NEON instructions") + list(APPEND PIXMAN_SOURCES + ${PIXMAN_SOURCES_DIR}/pixman/pixman-arm-neon.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-arm-neon-asm.S + ${PIXMAN_SOURCES_DIR}/pixman/pixman-arm-neon-asm-bilinear.S + ) + add_definitions( + -DUSE_ARM_NEON=1 + ) + elseif() + message("Processor without NEON instructions") + endif() + + add_definitions( + -DUSE_ARM_SIMD=1 + ) + list(APPEND PIXMAN_SOURCES + ${PIXMAN_SOURCES_DIR}/pixman/pixman-arm.c + ${PIXMAN_SOURCES_DIR}/pixman/pixman-arm-simd-asm.S + ${PIXMAN_SOURCES_DIR}/pixman/pixman-arm-simd-asm-scaled.S + ${PIXMAN_SOURCES_DIR}/pixman/pixman-arm-simd.c + ) + + ########################## + ## Portable Google NaCl + ########################## + + elseif (CMAKE_SYSTEM_NAME STREQUAL "PNaCl") + # No hardware acceleration + set(PIXMAN_DEFINITIONS "${PIXMAN_DEFINITIONS};TLS=__thread") + + else() + message(FATAL_ERROR "Support your platform here") + endif() + + + include_directories( + ${PIXMAN_SOURCES_DIR}/pixman + ) + + set_property( + SOURCE ${PIXMAN_SOURCES} + PROPERTY COMPILE_DEFINITIONS ${PIXMAN_DEFINITIONS} + ) + +else() + + pkg_search_module(PIXMAN REQUIRED pixman-1) + include_directories(${PIXMAN_INCLUDE_DIRS}) + link_libraries(${PIXMAN_LIBRARIES}) + +endif()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/CMake/PixmanConfiguration.patch Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,13 @@ +diff -urEb pixman-0.34.0.orig/pixman/pixman-x86.c pixman-0.34.0/pixman/pixman-x86.c +--- pixman-0.34.0.orig/pixman/pixman-x86.c 2016-07-05 12:46:52.889101224 +0200 ++++ pixman-0.34.0/pixman/pixman-x86.c 2016-07-05 12:47:07.253101808 +0200 +@@ -80,7 +80,7 @@ + static pixman_bool_t + have_cpuid (void) + { +-#if _PIXMAN_X86_64 || defined (_MSC_VER) ++#if _PIXMAN_X86_64 || defined (_MSC_VER) || defined(__native_client__) + + return TRUE; + +Only in pixman-0.34.0/pixman: pixman-x86.c~
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/CMake/SdlConfiguration.cmake Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,160 @@ +if (STATIC_BUILD OR NOT USE_SYSTEM_SDL) + SET(SDL_SOURCES_DIR ${CMAKE_BINARY_DIR}/SDL2-2.0.4) + SET(SDL_URL "http://www.montefiore.ulg.ac.be/~jodogne/Orthanc/ThirdPartyDownloads/Stone/SDL2-2.0.4.tar.gz") + SET(SDL_MD5 "44fc4a023349933e7f5d7a582f7b886e") + + DownloadPackage(${SDL_MD5} ${SDL_URL} "${SDL_SOURCES_DIR}") + + include_directories(${SDL_SOURCES_DIR}/include) + + set(TMP "${SDL_SOURCES_DIR}/include/SDL_config_premake.h") + if (NOT EXISTS "${TMP}") + file(WRITE "${TMP}" " +#include \"SDL_platform.h\" +#define HAVE_STDARG_H 1 +#define HAVE_STDDEF_H 1 +#define HAVE_STDINT_H 1 +") + endif() + + # General source files + file(GLOB SDL_SOURCES + ${SDL_SOURCES_DIR}/src/*.c + ${SDL_SOURCES_DIR}/src/atomic/*.c + ${SDL_SOURCES_DIR}/src/audio/*.c + ${SDL_SOURCES_DIR}/src/cpuinfo/*.c + ${SDL_SOURCES_DIR}/src/dynapi/*.c + ${SDL_SOURCES_DIR}/src/events/*.c + ${SDL_SOURCES_DIR}/src/file/*.c + ${SDL_SOURCES_DIR}/src/haptic/*.c + ${SDL_SOURCES_DIR}/src/joystick/*.c + ${SDL_SOURCES_DIR}/src/libm/*.c + ${SDL_SOURCES_DIR}/src/power/*.c + ${SDL_SOURCES_DIR}/src/render/*.c + ${SDL_SOURCES_DIR}/src/stdlib/*.c + ${SDL_SOURCES_DIR}/src/thread/*.c + ${SDL_SOURCES_DIR}/src/timer/*.c + ${SDL_SOURCES_DIR}/src/video/*.c + + ${SDL_SOURCES_DIR}/src/loadso/dummy/*.c + #${SDL_SOURCES_DIR}/src/timer/dummy/*.c + ${SDL_SOURCES_DIR}/src/audio/dummy/*.c + ${SDL_SOURCES_DIR}/src/filesystem/dummy/*.c + ${SDL_SOURCES_DIR}/src/haptic/dummy/*.c + ${SDL_SOURCES_DIR}/src/joystick/dummy/*.c + #${SDL_SOURCES_DIR}/src/main/dummy/*.c + ${SDL_SOURCES_DIR}/src/video/dummy/*.c + ) + + add_definitions( + -DUSING_PREMAKE_CONFIG_H=1 + + -DSDL_AUDIO_DISABLED=1 + -DSDL_AUDIO_DRIVER_DUMMY=1 + -DSDL_FILESYSTEM_DISABLED=1 + -DSDL_FILESYSTEM_DUMMY=1 + -DSDL_FILE_DISABLED=1 + -DSDL_HAPTIC_DISABLED=1 + -DSDL_JOYSTICK_DISABLED=1 + + #-DSDL_THREADS_DISABLED=1 + ) + + if (${CMAKE_SYSTEM_NAME} STREQUAL "Linux") + file(GLOB TMP + ${SDL_SOURCES_DIR}/src/core/linux/*.c + ${SDL_SOURCES_DIR}/src/loadso/dlopen/*.c + ${SDL_SOURCES_DIR}/src/render/opengl/*.c + ${SDL_SOURCES_DIR}/src/render/opengles2/*.c + ${SDL_SOURCES_DIR}/src/render/software/*.c + ${SDL_SOURCES_DIR}/src/thread/pthread/*.c + ${SDL_SOURCES_DIR}/src/timer/unix/*.c + ${SDL_SOURCES_DIR}/src/video/x11/*.c + ) + + list(APPEND SDL_SOURCES ${TMP}) + + add_definitions( + -DSDL_LOADSO_DLOPEN=1 + -DSDL_THREAD_PTHREAD=1 + -DSDL_TIMER_UNIX=1 + -DSDL_POWER_DISABLED=1 + + -DSDL_VIDEO_DRIVER_X11=1 + -DSDL_VIDEO_OPENGL=1 + -DSDL_VIDEO_OPENGL_ES2=1 + -DSDL_VIDEO_RENDER_OGL=1 + -DSDL_VIDEO_RENDER_OGL_ES2=1 + -DSDL_VIDEO_OPENGL_GLX=1 + -DSDL_VIDEO_OPENGL_EGL=1 + + -DSDL_ASSEMBLY_ROUTINES=1 + -DSDL_THREAD_PTHREAD_RECURSIVE_MUTEX=1 + -DSDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS=1 + -DHAVE_GCC_SYNC_LOCK_TEST_AND_SET=1 + ) + + link_libraries(X11 Xext) + + elseif (${CMAKE_SYSTEM_NAME} STREQUAL "Windows") + file(GLOB TMP + ${SDL_SOURCES_DIR}/src/audio/directsound/*.c + ${SDL_SOURCES_DIR}/src/audio/disk/*.c + ${SDL_SOURCES_DIR}/src/audio/winmm/*.c + ${SDL_SOURCES_DIR}/src/joystick/windows/*.c + ${SDL_SOURCES_DIR}/src/haptic/windows/*.c + ${SDL_SOURCES_DIR}/src/power/windows/*.c + + ${SDL_SOURCES_DIR}/src/main/windows/*.c + ${SDL_SOURCES_DIR}/src/core/windows/*.c + ${SDL_SOURCES_DIR}/src/loadso/windows/*.c + ${SDL_SOURCES_DIR}/src/render/direct3d/*.c + ${SDL_SOURCES_DIR}/src/render/direct3d11/*.c + ${SDL_SOURCES_DIR}/src/render/opengl/*.c + ${SDL_SOURCES_DIR}/src/render/psp/*.c + ${SDL_SOURCES_DIR}/src/render/opengles/*.c + ${SDL_SOURCES_DIR}/src/render/opengles2/*.c + ${SDL_SOURCES_DIR}/src/render/software/*.c + ${SDL_SOURCES_DIR}/src/thread/generic/SDL_syscond.c # Don't include more files from "thread/generic/*.c"! + ${SDL_SOURCES_DIR}/src/thread/windows/*.c + ${SDL_SOURCES_DIR}/src/timer/windows/*.c + ${SDL_SOURCES_DIR}/src/video/windows/*.c + ${SDL_SOURCES_DIR}/src/windows/dlopen/*.c + ) + + list(APPEND SDL_SOURCES ${TMP}) + + # NB: OpenGL ES headers are not available in MinGW-W64 + add_definitions( + -DSDL_LOADSO_WINDOWS=1 + -DSDL_THREAD_WINDOWS=1 + -DSDL_TIMER_WINDOWS=1 + -DSDL_POWER_WINDOWS=1 + + -DSDL_VIDEO_OPENGL=1 + -DSDL_VIDEO_OPENGL_WGL=1 + -DSDL_VIDEO_RENDER_D3D=1 + -DSDL_VIDEO_RENDER_OGL=1 + -DSDL_VIDEO_DRIVER_WINDOWS=1 + ) + + if (MSVC) + add_definitions( + -D__FLTUSED__ + -DHAVE_LIBC=1 + ) + else() + add_definitions( + -DHAVE_GCC_ATOMICS=1 + -DSDL_ASSEMBLY_ROUTINES=1 + ) + endif() + + link_libraries(imm32 winmm version) + endif() + +else() + pkg_search_module(SDL2 REQUIRED sdl2) + include_directories(${SDL2_INCLUDE_DIRS}) + link_libraries(${SDL2_LIBRARIES}) +endif()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/CMake/cairo-features.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,45 @@ +#ifndef CAIRO_FEATURES_H +#define CAIRO_FEATURES_H + +#define CAIRO_HAS_GOBJECT_FUNCTIONS 1 +#define CAIRO_HAS_IMAGE_SURFACE 1 +#define CAIRO_HAS_MIME_SURFACE 1 +#define CAIRO_HAS_OBSERVER_SURFACE 1 +#define CAIRO_HAS_RECORDING_SURFACE 1 +#define CAIRO_HAS_USER_FONT 1 + +/*#undef CAIRO_HAS_BEOS_SURFACE */ +/*#undef CAIRO_HAS_COGL_SURFACE */ +/*#undef CAIRO_HAS_DIRECTFB_SURFACE */ +/*#undef CAIRO_HAS_DRM_SURFACE */ +/*#undef CAIRO_HAS_EGL_FUNCTIONS */ +/*#undef CAIRO_HAS_FC_FONT */ +/*#undef CAIRO_HAS_FT_FONT */ +/*#undef CAIRO_HAS_GALLIUM_SURFACE */ +/*#undef CAIRO_HAS_GLESV2_SURFACE */ +/*#undef CAIRO_HAS_GLX_FUNCTIONS */ +/*#undef CAIRO_HAS_GL_SURFACE */ +/*#undef CAIRO_HAS_OS2_SURFACE */ +/*#undef CAIRO_HAS_PDF_SURFACE */ +/*#undef CAIRO_HAS_PNG_FUNCTIONS */ +/*#undef CAIRO_HAS_PS_SURFACE */ +/*#undef CAIRO_HAS_QT_SURFACE */ +/*#undef CAIRO_HAS_QUARTZ_FONT */ +/*#undef CAIRO_HAS_QUARTZ_IMAGE_SURFACE */ +/*#undef CAIRO_HAS_QUARTZ_SURFACE */ +/*#undef CAIRO_HAS_SCRIPT_SURFACE */ +/*#undef CAIRO_HAS_SKIA_SURFACE */ +/*#undef CAIRO_HAS_SVG_SURFACE */ +/*#undef CAIRO_HAS_TEE_SURFACE */ +/*#undef CAIRO_HAS_VG_SURFACE */ +/*#undef CAIRO_HAS_WGL_FUNCTIONS */ +/*#undef CAIRO_HAS_WIN32_FONT */ +/*#undef CAIRO_HAS_WIN32_SURFACE */ +/*#undef CAIRO_HAS_XCB_SHM_FUNCTIONS */ +/*#undef CAIRO_HAS_XCB_SURFACE */ +/*#undef CAIRO_HAS_XLIB_SURFACE */ +/*#undef CAIRO_HAS_XLIB_XCB_FUNCTIONS */ +/*#undef CAIRO_HAS_XLIB_XRENDER_SURFACE */ +/*#undef CAIRO_HAS_XML_SURFACE */ + +#endif
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/Colormaps/GenerateColormaps.py Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,36 @@ +#!/usr/bin/python + +import array +import matplotlib.pyplot as plt + +def GenerateColormap(name): + colormap = [] + + for gray in range(256): + if name == 'red': + color = (gray / 255.0, 0, 0) + elif name == 'green': + color = (0, gray / 255.0, 0) + elif name == 'blue': + color = (0, 0, gray / 255.0) + else: + color = plt.get_cmap(name) (gray) + + colormap += map(lambda k: int(round(color[k] * 255)), range(3)) + + colormap[0] = 0 + colormap[1] = 0 + colormap[2] = 0 + + return array.array('B', colormap).tostring() + + +for name in [ + 'hot', + 'jet', + 'blue', + 'green', + 'red', +]: + with open('%s.lut' % name, 'w') as f: + f.write(GenerateColormap(name))
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/OrthancStone.doxygen Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,1795 @@ +# Doxyfile 1.8.1.2 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (" "). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the config file +# that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# http://www.gnu.org/software/libiconv for the list of possible encodings. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or sequence of words) that should +# identify the project. Note that if you do not use Doxywizard you need +# to put quotes around the project name if it contains spaces. + +PROJECT_NAME = OrthancStone + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. +# This could be handy for archiving the generated documentation or +# if some version control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer +# a quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "Stone of Orthanc API" + +# With the PROJECT_LOGO tag one can specify an logo or icon that is +# included in the documentation. The maximum height of the logo should not +# exceed 55 pixels and the maximum width should not exceed 200 pixels. +# Doxygen will copy the logo to the output directory. + +PROJECT_LOGO = @ORTHANC_STONE_DIR@/Resources/OrthancLogoDocumentation.png + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) +# base path where the generated documentation will be put. +# If a relative path is entered, it will be relative to the location +# where doxygen was started. If left blank the current directory will be used. + +OUTPUT_DIRECTORY = OrthancStoneDocumentation + +# If the CREATE_SUBDIRS tag is set to YES, then doxygen will create +# 4096 sub-directories (in 2 levels) under the output directory of each output +# format and will distribute the generated files over these directories. +# Enabling this option can be useful when feeding doxygen a huge amount of +# source files, where putting all generated files in the same directory would +# otherwise cause performance problems for the file system. + +CREATE_SUBDIRS = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# The default language is English, other supported languages are: +# Afrikaans, Arabic, Brazilian, Catalan, Chinese, Chinese-Traditional, +# Croatian, Czech, Danish, Dutch, Esperanto, Farsi, Finnish, French, German, +# Greek, Hungarian, Italian, Japanese, Japanese-en (Japanese with English +# messages), Korean, Korean-en, Lithuanian, Norwegian, Macedonian, Persian, +# Polish, Portuguese, Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, +# Slovene, Spanish, Swedish, Ukrainian, and Vietnamese. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES (the default) Doxygen will +# include brief member descriptions after the members that are listed in +# the file and class documentation (similar to JavaDoc). +# Set to NO to disable this. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES (the default) Doxygen will prepend +# the brief description of a member or function before the detailed description. +# Note: if both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator +# that is used to form the text in various listings. Each string +# in this list, if found as the leading text of the brief description, will be +# stripped from the text and the result after processing the whole list, is +# used as the annotated text. Otherwise, the brief description is used as-is. +# If left blank, the following values are used ("$name" is automatically +# replaced with the name of the entity): "The $name class" "The $name widget" +# "The $name file" "is" "provides" "specifies" "contains" +# "represents" "a" "an" "the" + +ABBREVIATE_BRIEF = + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# Doxygen will generate a detailed section even if there is only a brief +# description. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES then Doxygen will prepend the full +# path before files name in the file list and in the header files. If set +# to NO the shortest path that makes the file name unique will be used. + +FULL_PATH_NAMES = NO + +# If the FULL_PATH_NAMES tag is set to YES then the STRIP_FROM_PATH tag +# can be used to strip a user-defined part of the path. Stripping is +# only done if one of the specified strings matches the left-hand part of +# the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the +# path to strip. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of +# the path mentioned in the documentation of a class, which tells +# the reader which header file to include in order to use a class. +# If left blank only the name of the header file containing the class +# definition is used. Otherwise one should specify the include paths that +# are normally passed to the compiler using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter +# (but less readable) file names. This can be useful if your file system +# doesn't support long names like on DOS, Mac, or CD-ROM. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then Doxygen +# will interpret the first line (until the first dot) of a JavaDoc-style +# comment as the brief description. If set to NO, the JavaDoc +# comments will behave just like regular Qt-style comments +# (thus requiring an explicit @brief command for a brief description.) + +JAVADOC_AUTOBRIEF = NO + +# If the QT_AUTOBRIEF tag is set to YES then Doxygen will +# interpret the first line (until the first dot) of a Qt-style +# comment as the brief description. If set to NO, the comments +# will behave just like regular Qt-style comments (thus requiring +# an explicit \brief command for a brief description.) + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make Doxygen +# treat a multi-line C++ special comment block (i.e. a block of //! or /// +# comments) as a brief description. This used to be the default behaviour. +# The new default is to treat a multi-line C++ comment block as a detailed +# description. Set this tag to YES if you prefer the old behaviour instead. + +MULTILINE_CPP_IS_BRIEF = NO + +# If the INHERIT_DOCS tag is set to YES (the default) then an undocumented +# member inherits the documentation from any documented member that it +# re-implements. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES, then doxygen will produce +# a new page for each member. If set to NO, the documentation of a member will +# be part of the file/class/namespace that contains it. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. +# Doxygen uses this value to replace tabs by spaces in code fragments. + +TAB_SIZE = 8 + +# This tag can be used to specify a number of aliases that acts +# as commands in the documentation. An alias has the form "name=value". +# For example adding "sideeffect=\par Side Effects:\n" will allow you to +# put the command \sideeffect (or @sideeffect) in the documentation, which +# will result in a user-defined paragraph with heading "Side Effects:". +# You can put \n's in the value part of an alias to insert newlines. + +ALIASES = + +# This tag can be used to specify a number of word-keyword mappings (TCL only). +# A mapping has the form "name=value". For example adding +# "class=itcl::class" will allow you to use the command class in the +# itcl::class meaning. + +TCL_SUBST = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C +# sources only. Doxygen will then generate output that is more tailored for C. +# For instance, some of the names that are used will be different. The list +# of all members will be omitted, etc. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java +# sources only. Doxygen will then generate output that is more tailored for +# Java. For instance, namespaces will be presented as packages, qualified +# scopes will look different, etc. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources only. Doxygen will then generate output that is more tailored for +# Fortran. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for +# VHDL. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given extension. +# Doxygen has a built-in mapping, but you can override or extend it using this +# tag. The format is ext=language, where ext is a file extension, and language +# is one of the parsers supported by doxygen: IDL, Java, Javascript, CSharp, C, +# C++, D, PHP, Objective-C, Python, Fortran, VHDL, C, C++. For instance to make +# doxygen treat .inc files as Fortran files (default is PHP), and .f files as C +# (default is Fortran), use: inc=Fortran f=C. Note that for custom extensions +# you also need to set FILE_PATTERNS otherwise the files are not read by doxygen. + +EXTENSION_MAPPING = + +# If MARKDOWN_SUPPORT is enabled (the default) then doxygen pre-processes all +# comments according to the Markdown format, which allows for more readable +# documentation. See http://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you +# can mix doxygen, HTML, and XML commands with Markdown formatting. +# Disable only in case of backward compatibilities issues. + +MARKDOWN_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should +# set this tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); v.s. +# func(std::string) {}). This also makes the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. + +BUILTIN_STL_SUPPORT = YES + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip sources only. +# Doxygen will parse them like normal C++ but will assume all classes use public +# instead of private inheritance when no explicit protection keyword is present. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate getter +# and setter methods for a property. Setting this option to YES (the default) +# will make doxygen replace the get and set methods by a property in the +# documentation. This will only work if the methods are indeed getting or +# setting a simple type. If this is not the case, or you want to show the +# methods anyway, you should set this option to NO. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES, then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. + +DISTRIBUTE_GROUP_DOC = NO + +# Set the SUBGROUPING tag to YES (the default) to allow class member groups of +# the same type (for instance a group of public functions) to be put as a +# subgroup of that type (e.g. under the Public Functions section). Set it to +# NO to prevent subgrouping. Alternatively, this can be done per class using +# the \nosubgrouping command. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and +# unions are shown inside the group in which they are included (e.g. using +# @ingroup) instead of on a separate page (for HTML and Man pages) or +# section (for LaTeX and RTF). + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and +# unions with only public data fields will be shown inline in the documentation +# of the scope in which they are defined (i.e. file, namespace, or group +# documentation), provided this scope is documented. If set to NO (the default), +# structs, classes, and unions are shown on a separate page (for HTML and Man +# pages) or section (for LaTeX and RTF). + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT is enabled, a typedef of a struct, union, or enum +# is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically +# be useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. + +TYPEDEF_HIDES_STRUCT = NO + +# The SYMBOL_CACHE_SIZE determines the size of the internal cache use to +# determine which symbols to keep in memory and which to flush to disk. +# When the cache is full, less often used symbols will be written to disk. +# For small to medium size projects (<1000 input files) the default value is +# probably good enough. For larger projects a too small cache size can cause +# doxygen to be busy swapping symbols to and from disk most of the time +# causing a significant performance penalty. +# If the system has enough physical memory increasing the cache will improve the +# performance by keeping more symbols in memory. Note that the value works on +# a logarithmic scale so increasing the size by one will roughly double the +# memory usage. The cache size is given by this formula: +# 2^(16+SYMBOL_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +SYMBOL_CACHE_SIZE = 0 + +# Similar to the SYMBOL_CACHE_SIZE the size of the symbol lookup cache can be +# set using LOOKUP_CACHE_SIZE. This cache is used to resolve symbols given +# their name and scope. Since this can be an expensive process and often the +# same symbol appear multiple times in the code, doxygen keeps a cache of +# pre-resolved symbols. If the cache is too small doxygen will become slower. +# If the cache is too large, memory is wasted. The cache size is given by this +# formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range is 0..9, the default is 0, +# corresponding to a cache size of 2^16 = 65536 symbols. + +LOOKUP_CACHE_SIZE = 0 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES doxygen will assume all entities in +# documentation are documented, even if no documentation was available. +# Private class members and static file members will be hidden unless +# the EXTRACT_PRIVATE and EXTRACT_STATIC tags are set to YES + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES all private members of a class +# will be included in the documentation. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PACKAGE tag is set to YES all members with package or internal scope will be included in the documentation. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES all static members of a file +# will be included in the documentation. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES classes (and structs) +# defined locally in source files will be included in the documentation. +# If set to NO only classes defined in header files are included. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. When set to YES local +# methods, which are defined in the implementation section but not in +# the interface are included in the documentation. +# If set to NO (the default) only methods in the interface are included. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base +# name of the file that contains the anonymous namespace. By default +# anonymous namespaces are hidden. + +EXTRACT_ANON_NSPACES = NO + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, Doxygen will hide all +# undocumented members of documented classes, files or namespaces. +# If set to NO (the default) these members will be included in the +# various overviews, but no documentation section is generated. +# This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, Doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. +# If set to NO (the default) these classes will be included in the various +# overviews. This option has no effect if EXTRACT_ALL is enabled. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, Doxygen will hide all +# friend (class|struct|union) declarations. +# If set to NO (the default) these declarations will be included in the +# documentation. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, Doxygen will hide any +# documentation blocks found inside the body of a function. +# If set to NO (the default) these blocks will be appended to the +# function's detailed documentation block. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation +# that is typed after a \internal command is included. If the tag is set +# to NO (the default) then the documentation will be excluded. +# Set it to YES to include the internal documentation. + +INTERNAL_DOCS = NO + +# If the CASE_SENSE_NAMES tag is set to NO then Doxygen will only generate +# file names in lower-case letters. If set to YES upper-case letters are also +# allowed. This is useful if you have classes or files whose names only differ +# in case and if your file system supports case sensitive file names. Windows +# and Mac users are advised to set this option to NO. + +CASE_SENSE_NAMES = YES + +# If the HIDE_SCOPE_NAMES tag is set to NO (the default) then Doxygen +# will show members with their full class and namespace scopes in the +# documentation. If set to YES the scope will be hidden. + +HIDE_SCOPE_NAMES = NO + +# If the SHOW_INCLUDE_FILES tag is set to YES (the default) then Doxygen +# will put a list of the files that are included by a file in the documentation +# of that file. + +SHOW_INCLUDE_FILES = YES + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then Doxygen +# will list include files with double quotes in the documentation +# rather than with sharp brackets. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES (the default) then a tag [inline] +# is inserted in the documentation for inline members. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES (the default) then doxygen +# will sort the (detailed) documentation of file and class members +# alphabetically by member name. If set to NO the members will appear in +# declaration order. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the +# brief documentation of file, namespace and class members alphabetically +# by member name. If set to NO (the default) the members will appear in +# declaration order. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen +# will sort the (brief and detailed) documentation of class members so that +# constructors and destructors are listed first. If set to NO (the default) +# the constructors will appear in the respective orders defined by +# SORT_MEMBER_DOCS and SORT_BRIEF_DOCS. +# This tag will be ignored for brief docs if SORT_BRIEF_DOCS is set to NO +# and ignored for detailed docs if SORT_MEMBER_DOCS is set to NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the +# hierarchy of group names into alphabetical order. If set to NO (the default) +# the group names will appear in their defined order. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be +# sorted by fully-qualified names, including namespaces. If set to +# NO (the default), the class list will be sorted only by class name, +# not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the +# alphabetical list. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to +# do proper type resolution of all parameters of a function it will reject a +# match between the prototype and the implementation of a member function even +# if there is only one candidate or it is obvious which candidate to choose +# by doing a simple string match. By disabling STRICT_PROTO_MATCHING doxygen +# will still accept a match between prototype and implementation in such cases. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or +# disable (NO) the todo list. This list is created by putting \todo +# commands in the documentation. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or +# disable (NO) the test list. This list is created by putting \test +# commands in the documentation. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or +# disable (NO) the bug list. This list is created by putting \bug +# commands in the documentation. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or +# disable (NO) the deprecated list. This list is created by putting +# \deprecated commands in the documentation. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional +# documentation sections, marked by \if sectionname ... \endif. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines +# the initial value of a variable or macro consists of for it to appear in +# the documentation. If the initializer consists of more lines than specified +# here it will be hidden. Use a value of 0 to hide initializers completely. +# The appearance of the initializer of individual variables and macros in the +# documentation can be controlled using \showinitializer or \hideinitializer +# command in the documentation regardless of this setting. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated +# at the bottom of the documentation of classes and structs. If set to YES the +# list will mention the files that were used to generate the documentation. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. +# This will remove the Files entry from the Quick Index and from the +# Folder Tree View (if specified). The default is YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the +# Namespaces page. +# This will remove the Namespaces entry from the Quick Index +# and from the Folder Tree View (if specified). The default is YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command <command> <input-file>, where <command> is the value of +# the FILE_VERSION_FILTER tag, and <input-file> is the name of an input file +# provided by doxygen. Whatever the program writes to standard output +# is used as the file version. See the manual for examples. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. +# You can optionally specify a file name after the option, if omitted +# DoxygenLayout.xml will be used as the name of the layout file. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files +# containing the references data. This must be a list of .bib files. The +# .bib extension is automatically appended if omitted. Using this command +# requires the bibtex tool to be installed. See also +# http://en.wikipedia.org/wiki/BibTeX for more info. For LaTeX the style +# of the bibliography can be controlled using LATEX_BIB_STYLE. To use this +# feature you need bibtex and perl available in the search path. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated +# by doxygen. Possible values are YES and NO. If left blank NO is used. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated by doxygen. Possible values are YES and NO. If left blank +# NO is used. + +WARNINGS = YES + +# If WARN_IF_UNDOCUMENTED is set to YES, then doxygen will generate warnings +# for undocumented members. If EXTRACT_ALL is set to YES then this flag will +# automatically be disabled. + +WARN_IF_UNDOCUMENTED = YES + +# If WARN_IF_DOC_ERROR is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some +# parameters in a documented function, or documenting parameters that +# don't exist or using markup commands wrongly. + +WARN_IF_DOC_ERROR = YES + +# The WARN_NO_PARAMDOC option can be enabled to get warnings for +# functions that are documented, but have no documentation for their parameters +# or return value. If set to NO (the default) doxygen will only warn about +# wrong or incomplete parameter documentation, but not about the absence of +# documentation. + +WARN_NO_PARAMDOC = NO + +# The WARN_FORMAT tag determines the format of the warning messages that +# doxygen can produce. The string should contain the $file, $line, and $text +# tags, which will be replaced by the file and line number from which the +# warning originated and the warning text. Optionally the format may contain +# $version, which will be replaced by the version of the file (if it could +# be obtained via FILE_VERSION_FILTER) + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning +# and error messages should be written. If left blank the output is written +# to stderr. + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag can be used to specify the files and/or directories that contain +# documented source files. You may enter file names like "myfile.cpp" or +# directories like "/usr/src/myproject". Separate the files or directories +# with spaces. + +INPUT = @ORTHANC_STONE_DIR@/Framework \ + @ORTHANC_STONE_DIR@/Tests/Sdl/dev.h + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is +# also the default input encoding. Doxygen uses libiconv (or the iconv built +# into libc) for the transcoding. See http://www.gnu.org/software/libiconv for +# the list of possible encodings. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank the following patterns are tested: +# *.c *.cc *.cxx *.cpp *.c++ *.d *.java *.ii *.ixx *.ipp *.i++ *.inl *.h *.hh +# *.hxx *.hpp *.h++ *.idl *.odl *.cs *.php *.php3 *.inc *.m *.mm *.dox *.py +# *.f90 *.f *.for *.vhd *.vhdl + +FILE_PATTERNS = *.h + +# The RECURSIVE tag can be used to turn specify whether or not subdirectories +# should be searched for input files as well. Possible values are YES and NO. +# If left blank NO is used. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = @ORTHANC_STONE_DIR@/Framework/Orthanc/Plugins/ \ + @ORTHANC_STONE_DIR@/Framework/Orthanc/Resources/ \ + @ORTHANC_STONE_DIR@/Framework/Orthanc/Sdk-1.1.0/ + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. Note that the wildcards are matched +# against the file with absolute path, so to exclude all test directories +# for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test + +EXCLUDE_SYMBOLS = Orthanc::Internals + +# The EXAMPLE_PATH tag can be used to specify one or more files or +# directories that contain example code fragments that are included (see +# the \include command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp +# and *.h) to filter out the source-files in the directories. If left +# blank all files are included. + +EXAMPLE_PATTERNS = + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude +# commands irrespective of the value of the RECURSIVE tag. +# Possible values are YES and NO. If left blank NO is used. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or +# directories that contain image that are included in the documentation (see +# the \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command <filter> <input-file>, where <filter> +# is the value of the INPUT_FILTER tag, and <input-file> is the name of an +# input file. Doxygen will then use the output that the filter program writes +# to standard output. +# If FILTER_PATTERNS is specified, this tag will be +# ignored. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. +# Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. +# The filters are a list of the form: +# pattern=filter (like *.cpp=my_cpp_filter). See INPUT_FILTER for further +# info on how filters are used. If FILTER_PATTERNS is empty or if +# non of the patterns match the file name, INPUT_FILTER is applied. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will be used to filter the input files when producing source +# files to browse (i.e. when SOURCE_BROWSER is set to YES). + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) +# and it is also possible to disable source filtering for a specific pattern +# using *.ext= (so without naming a filter). This option only has effect when +# FILTER_SOURCE_FILES is enabled. + +FILTER_SOURCE_PATTERNS = + +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will +# be generated. Documented entities will be cross-referenced with these sources. +# Note: To get rid of all source code in the generated output, make sure also +# VERBATIM_HEADERS is set to NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body +# of functions and classes directly in the documentation. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES (the default) will instruct +# doxygen to hide any special comment blocks from generated source code +# fragments. Normal C, C++ and Fortran comments will always remain visible. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES +# then for each documented function all documented +# functions referencing it will be listed. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES +# then for each documented function all documented entities +# called/used by that function will be listed. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES (the default) +# and SOURCE_BROWSER tag is set to YES, then the hyperlinks from +# functions in REFERENCES_RELATION and REFERENCED_BY_RELATION lists will +# link to the source code. +# Otherwise they will link to the documentation. + +REFERENCES_LINK_SOURCE = YES + +# If the USE_HTAGS tag is set to YES then the references to source code +# will point to the HTML generated by the htags(1) tool instead of doxygen +# built-in source browser. The htags tool is part of GNU's global source +# tagging system (see http://www.gnu.org/software/global/global.html). You +# will need version 4.8.6 or higher. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set to YES (the default) then Doxygen +# will generate a verbatim copy of the header file for each class for +# which an include is specified. Set to NO to disable this. + +VERBATIM_HEADERS = YES + +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index +# of all compounds will be generated. Enable this if the project +# contains a lot of classes, structs, unions or interfaces. + +ALPHABETICAL_INDEX = YES + +# If the alphabetical index is enabled (see ALPHABETICAL_INDEX) then +# the COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns +# in which this list will be split (can be a number in the range [1..20]) + +COLS_IN_ALPHA_INDEX = 5 + +# In case all classes in a project start with a common prefix, all +# classes will be put under the same header in the alphabetical index. +# The IGNORE_PREFIX tag can be used to specify one or more prefixes that +# should be ignored while generating the index headers. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES (the default) Doxygen will +# generate HTML output. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `html' will be used as the default path. + +HTML_OUTPUT = doc + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for +# each generated HTML page (for example: .htm,.php,.asp). If it is left blank +# doxygen will generate files with .html extension. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a personal HTML header for +# each generated HTML page. If it is left blank doxygen will generate a +# standard header. Note that when using a custom header you are responsible +# for the proper inclusion of any scripts and style sheets that doxygen +# needs, which is dependent on the configuration options used. +# It is advised to generate a default header using "doxygen -w html +# header.html footer.html stylesheet.css YourConfigFile" and then modify +# that header. Note that the header is subject to change so you typically +# have to redo this when upgrading to a newer version of doxygen or when +# changing the value of configuration settings such as GENERATE_TREEVIEW! + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a personal HTML footer for +# each generated HTML page. If it is left blank doxygen will generate a +# standard footer. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading +# style sheet that is used by each HTML page. It can be used to +# fine-tune the look of the HTML output. If the tag is left blank doxygen +# will generate a default style sheet. Note that doxygen will try to copy +# the style sheet file to the HTML output directory, so don't put your own +# style sheet in the HTML output directory as well, or it will be erased! + +HTML_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath$ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that +# the files will be copied as-is; there are no commands or markers available. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. +# Doxygen will adjust the colors in the style sheet and background images +# according to this color. Hue is specified as an angle on a colorwheel, +# see http://en.wikipedia.org/wiki/Hue for more information. +# For instance the value 0 represents red, 60 is yellow, 120 is green, +# 180 is cyan, 240 is blue, 300 purple, and 360 is red again. +# The allowed range is 0 to 359. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of +# the colors in the HTML output. For a value of 0 the output will use +# grayscales only. A value of 255 will produce the most vivid colors. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to +# the luminance component of the colors in the HTML output. Values below +# 100 gradually make the output lighter, whereas values above 100 make +# the output darker. The value divided by 100 is the actual gamma applied, +# so 80 represents a gamma of 0.8, The value 220 represents a gamma of 2.2, +# and 100 does not change the gamma. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting +# this to NO can help when comparing the output of multiple runs. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of +# entries shown in the various tree structured indices initially; the user +# can expand and collapse entries dynamically later on. Doxygen will expand +# the tree to such a level that at most the specified number of entries are +# visible (unless a fully collapsed tree already exceeds this amount). +# So setting the number of entries 1 will produce a full collapsed tree by +# default. 0 is a special value representing an infinite number of entries +# and will result in a full expanded tree by default. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files +# will be generated that can be used as input for Apple's Xcode 3 +# integrated development environment, introduced with OSX 10.5 (Leopard). +# To create a documentation set, doxygen will generate a Makefile in the +# HTML output directory. Running make will produce the docset in that +# directory and running "make install" will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find +# it at startup. +# See http://developer.apple.com/tools/creatingdocsetswithdoxygen.html +# for more information. + +GENERATE_DOCSET = NO + +# When GENERATE_DOCSET tag is set to YES, this tag determines the name of the +# feed. A documentation feed provides an umbrella under which multiple +# documentation sets from a single provider (such as a company or product suite) +# can be grouped. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# When GENERATE_DOCSET tag is set to YES, this tag specifies a string that +# should uniquely identify the documentation set bundle. This should be a +# reverse domain-name style string, e.g. com.mycompany.MyDocSet. Doxygen +# will append .docset to the name. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# When GENERATE_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The GENERATE_PUBLISHER_NAME tag identifies the documentation publisher. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES, additional index files +# will be generated that can be used as input for tools like the +# Microsoft HTML help workshop to generate a compiled HTML help file (.chm) +# of the generated HTML documentation. + +GENERATE_HTMLHELP = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_FILE tag can +# be used to specify the file name of the resulting .chm file. You +# can add a path in front of the file if the result should not be +# written to the html output directory. + +CHM_FILE = + +# If the GENERATE_HTMLHELP tag is set to YES, the HHC_LOCATION tag can +# be used to specify the location (absolute path including file name) of +# the HTML help compiler (hhc.exe). If non-empty doxygen will try to run +# the HTML help compiler on the generated index.hhp. + +HHC_LOCATION = + +# If the GENERATE_HTMLHELP tag is set to YES, the GENERATE_CHI flag +# controls if a separate .chi index file is generated (YES) or that +# it should be included in the master .chm file (NO). + +GENERATE_CHI = NO + +# If the GENERATE_HTMLHELP tag is set to YES, the CHM_INDEX_ENCODING +# is used to encode HtmlHelp index (hhk), content (hhc) and project file +# content. + +CHM_INDEX_ENCODING = + +# If the GENERATE_HTMLHELP tag is set to YES, the BINARY_TOC flag +# controls whether a binary table of contents is generated (YES) or a +# normal table of contents (NO) in the .chm file. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members +# to the contents of the HTML help documentation and to the tree view. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated +# that can be used as input for Qt's qhelpgenerator to generate a +# Qt Compressed Help (.qch) of the generated HTML documentation. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can +# be used to specify the file name of the resulting .qch file. +# The path specified is relative to the HTML output folder. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#namespace + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating +# Qt Help Project output. For more information please see +# http://doc.trolltech.com/qthelpproject.html#virtual-folders + +QHP_VIRTUAL_FOLDER = doc + +# If QHP_CUST_FILTER_NAME is set, it specifies the name of a custom filter to +# add. For more information please see +# http://doc.trolltech.com/qthelpproject.html#custom-filters + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILT_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see +# <a href="http://doc.trolltech.com/qthelpproject.html#custom-filters"> +# Qt Help Project / Custom Filters</a>. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's +# filter section matches. +# <a href="http://doc.trolltech.com/qthelpproject.html#filter-attributes"> +# Qt Help Project / Filter Attributes</a>. + +QHP_SECT_FILTER_ATTRS = + +# If the GENERATE_QHP tag is set to YES, the QHG_LOCATION tag can +# be used to specify the location of Qt's qhelpgenerator. +# If non-empty doxygen will try to run qhelpgenerator on the generated +# .qhp file. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files +# will be generated, which together with the HTML files, form an Eclipse help +# plugin. To install this plugin and make it available under the help contents +# menu in Eclipse, the contents of the directory containing the HTML and XML +# files needs to be copied into the plugins directory of eclipse. The name of +# the directory within the plugins directory should be the same as +# the ECLIPSE_DOC_ID value. After copying Eclipse needs to be restarted before +# the help appears. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have +# this name. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# The DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) +# at top of each HTML page. The value NO (the default) enables the index and +# the value YES disables it. Since the tabs have the same information as the +# navigation tree you can set this option to NO if you already set +# GENERATE_TREEVIEW to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. +# If the tag value is set to YES, a side panel will be generated +# containing a tree-like index structure (just like the one that +# is generated for HTML Help). For this to work a browser that supports +# JavaScript, DHTML, CSS and frames is required (i.e. any modern browser). +# Windows users are probably better off using the HTML help feature. +# Since the tree basically has the same information as the tab index you +# could consider to set DISABLE_INDEX to NO when enabling this option. + +GENERATE_TREEVIEW = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values +# (range [0,1..20]) that doxygen will group on one line in the generated HTML +# documentation. Note that a value of 0 will completely suppress the enum +# values from appearing in the overview section. + +ENUM_VALUES_PER_LINE = 1 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be +# used to set the initial width (in pixels) of the frame in which the tree +# is shown. + +TREEVIEW_WIDTH = 250 + +# When the EXT_LINKS_IN_WINDOW option is set to YES doxygen will open +# links to external symbols imported via tag files in a separate window. + +EXT_LINKS_IN_WINDOW = NO + +# Use this tag to change the font size of Latex formulas included +# as images in the HTML documentation. The default is 10. Note that +# when you change the font size after a successful doxygen run you need +# to manually remove any form_*.png images from the HTML output directory +# to force them to be regenerated. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are +# not supported properly for IE 6.0, but are supported on all modern browsers. +# Note that when changing this option you need to delete any form_*.png files +# in the HTML output before the changes have effect. + +FORMULA_TRANSPARENT = YES + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax +# (see http://www.mathjax.org) which uses client side Javascript for the +# rendering instead of using prerendered bitmaps. Use this if you do not +# have LaTeX installed or if you want to formulas look prettier in the HTML +# output. When enabled you may also need to install MathJax separately and +# configure the path to it using the MATHJAX_RELPATH option. + +USE_MATHJAX = NO + +# When MathJax is enabled you need to specify the location relative to the +# HTML output directory using the MATHJAX_RELPATH option. The destination +# directory should contain the MathJax.js script. For instance, if the mathjax +# directory is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to +# the MathJax Content Delivery Network so you can quickly see the result without +# installing MathJax. +# However, it is strongly recommended to install a local +# copy of MathJax from http://www.mathjax.org before deployment. + +MATHJAX_RELPATH = http://www.mathjax.org/mathjax + +# The MATHJAX_EXTENSIONS tag can be used to specify one or MathJax extension +# names that should be enabled during MathJax rendering. + +MATHJAX_EXTENSIONS = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box +# for the HTML output. The underlying search engine uses javascript +# and DHTML and should work on any modern browser. Note that when using +# HTML help (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets +# (GENERATE_DOCSET) there is already a search function so this one should +# typically be disabled. For large projects the javascript based search engine +# can be slow, then enabling SERVER_BASED_SEARCH may provide a better solution. + +SEARCHENGINE = YES + +# When the SERVER_BASED_SEARCH tag is enabled the search engine will be +# implemented using a PHP enabled web server instead of at the web client +# using Javascript. Doxygen will generate the search PHP script and index +# file to put on the web server. The advantage of the server +# based approach is that it scales better to large projects and allows +# full text search. The disadvantages are that it is more difficult to setup +# and does not have live searching capabilities. + +SERVER_BASED_SEARCH = NO + +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- + +# If the GENERATE_LATEX tag is set to YES (the default) Doxygen will +# generate Latex output. + +GENERATE_LATEX = NO + +# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `latex' will be used as the default path. + +LATEX_OUTPUT = latex + +# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be +# invoked. If left blank `latex' will be used as the default command name. +# Note that when enabling USE_PDFLATEX this option is only used for +# generating bitmaps for formulas in the HTML output, but not in the +# Makefile that is written to the output directory. + +LATEX_CMD_NAME = latex + +# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to +# generate index for LaTeX. If left blank `makeindex' will be used as the +# default command name. + +MAKEINDEX_CMD_NAME = makeindex + +# If the COMPACT_LATEX tag is set to YES Doxygen generates more compact +# LaTeX documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_LATEX = NO + +# The PAPER_TYPE tag can be used to set the paper type that is used +# by the printer. Possible values are: a4, letter, legal and +# executive. If left blank a4wide will be used. + +PAPER_TYPE = a4 + +# The EXTRA_PACKAGES tag can be to specify one or more names of LaTeX +# packages that should be included in the LaTeX output. + +EXTRA_PACKAGES = + +# The LATEX_HEADER tag can be used to specify a personal LaTeX header for +# the generated latex document. The header should contain everything until +# the first chapter. If it is left blank doxygen will generate a +# standard header. Notice: only use this tag if you know what you are doing! + +LATEX_HEADER = + +# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for +# the generated latex document. The footer should contain everything after +# the last chapter. If it is left blank doxygen will generate a +# standard footer. Notice: only use this tag if you know what you are doing! + +LATEX_FOOTER = + +# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated +# is prepared for conversion to pdf (using ps2pdf). The pdf file will +# contain links (just like the HTML output) instead of page references +# This makes the output suitable for online browsing using a pdf viewer. + +PDF_HYPERLINKS = YES + +# If the USE_PDFLATEX tag is set to YES, pdflatex will be used instead of +# plain latex in the generated Makefile. Set this option to YES to get a +# higher quality PDF documentation. + +USE_PDFLATEX = YES + +# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \\batchmode. +# command to the generated LaTeX files. This will instruct LaTeX to keep +# running if errors occur, instead of asking the user for help. +# This option is also used when generating formulas in HTML. + +LATEX_BATCHMODE = NO + +# If LATEX_HIDE_INDICES is set to YES then doxygen will not +# include the index chapters (such as File Index, Compound Index, etc.) +# in the output. + +LATEX_HIDE_INDICES = NO + +# If LATEX_SOURCE_CODE is set to YES then doxygen will include +# source code with syntax highlighting in the LaTeX output. +# Note that which sources are shown also depends on other settings +# such as SOURCE_BROWSER. + +LATEX_SOURCE_CODE = NO + +# The LATEX_BIB_STYLE tag can be used to specify the style to use for the +# bibliography, e.g. plainnat, or ieeetr. The default style is "plain". See +# http://en.wikipedia.org/wiki/BibTeX for more info. + +LATEX_BIB_STYLE = plain + +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- + +# If the GENERATE_RTF tag is set to YES Doxygen will generate RTF output +# The RTF output is optimized for Word 97 and may not look very pretty with +# other RTF readers or editors. + +GENERATE_RTF = NO + +# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `rtf' will be used as the default path. + +RTF_OUTPUT = rtf + +# If the COMPACT_RTF tag is set to YES Doxygen generates more compact +# RTF documents. This may be useful for small projects and may help to +# save some trees in general. + +COMPACT_RTF = NO + +# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated +# will contain hyperlink fields. The RTF file will +# contain links (just like the HTML output) instead of page references. +# This makes the output suitable for online browsing using WORD or other +# programs which support those fields. +# Note: wordpad (write) and others do not support links. + +RTF_HYPERLINKS = NO + +# Load style sheet definitions from file. Syntax is similar to doxygen's +# config file, i.e. a series of assignments. You only have to provide +# replacements, missing definitions are set to their default value. + +RTF_STYLESHEET_FILE = + +# Set optional variables used in the generation of an rtf document. +# Syntax is similar to doxygen's config file. + +RTF_EXTENSIONS_FILE = + +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- + +# If the GENERATE_MAN tag is set to YES (the default) Doxygen will +# generate man pages + +GENERATE_MAN = NO + +# The MAN_OUTPUT tag is used to specify where the man pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `man' will be used as the default path. + +MAN_OUTPUT = man + +# The MAN_EXTENSION tag determines the extension that is added to +# the generated man pages (default is the subroutine's section .3) + +MAN_EXTENSION = .3 + +# If the MAN_LINKS tag is set to YES and Doxygen generates man output, +# then it will generate one additional man file for each entity +# documented in the real man page(s). These additional files +# only source the real man page, but without them the man command +# would be unable to find the correct page. The default is NO. + +MAN_LINKS = NO + +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- + +# If the GENERATE_XML tag is set to YES Doxygen will +# generate an XML file that captures the structure of +# the code including all documentation. + +GENERATE_XML = NO + +# The XML_OUTPUT tag is used to specify where the XML pages will be put. +# If a relative path is entered the value of OUTPUT_DIRECTORY will be +# put in front of it. If left blank `xml' will be used as the default path. + +XML_OUTPUT = xml + +# The XML_SCHEMA tag can be used to specify an XML schema, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_SCHEMA = + +# The XML_DTD tag can be used to specify an XML DTD, +# which can be used by a validating XML parser to check the +# syntax of the XML files. + +XML_DTD = + +# If the XML_PROGRAMLISTING tag is set to YES Doxygen will +# dump the program listings (including syntax highlighting +# and cross-referencing information) to the XML output. Note that +# enabling this will significantly increase the size of the XML output. + +XML_PROGRAMLISTING = YES + +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- + +# If the GENERATE_AUTOGEN_DEF tag is set to YES Doxygen will +# generate an AutoGen Definitions (see autogen.sf.net) file +# that captures the structure of the code including all +# documentation. Note that this feature is still experimental +# and incomplete at the moment. + +GENERATE_AUTOGEN_DEF = NO + +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- + +# If the GENERATE_PERLMOD tag is set to YES Doxygen will +# generate a Perl module file that captures the structure of +# the code including all documentation. Note that this +# feature is still experimental and incomplete at the +# moment. + +GENERATE_PERLMOD = NO + +# If the PERLMOD_LATEX tag is set to YES Doxygen will generate +# the necessary Makefile rules, Perl scripts and LaTeX code to be able +# to generate PDF and DVI output from the Perl module output. + +PERLMOD_LATEX = NO + +# If the PERLMOD_PRETTY tag is set to YES the Perl module output will be +# nicely formatted so it can be parsed by a human reader. +# This is useful +# if you want to understand what is going on. +# On the other hand, if this +# tag is set to NO the size of the Perl module output will be much smaller +# and Perl will parse it just the same. + +PERLMOD_PRETTY = YES + +# The names of the make variables in the generated doxyrules.make file +# are prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. +# This is useful so different doxyrules.make files included by the same +# Makefile don't overwrite each other's variables. + +PERLMOD_MAKEVAR_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- + +# If the ENABLE_PREPROCESSING tag is set to YES (the default) Doxygen will +# evaluate all C-preprocessor directives found in the sources and include +# files. + +ENABLE_PREPROCESSING = YES + +# If the MACRO_EXPANSION tag is set to YES Doxygen will expand all macro +# names in the source code. If set to NO (the default) only conditional +# compilation will be performed. Macro expansion can be done in a controlled +# way by setting EXPAND_ONLY_PREDEF to YES. + +MACRO_EXPANSION = NO + +# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES +# then the macro expansion is limited to the macros specified with the +# PREDEFINED and EXPAND_AS_DEFINED tags. + +EXPAND_ONLY_PREDEF = NO + +# If the SEARCH_INCLUDES tag is set to YES (the default) the includes files +# pointed to by INCLUDE_PATH will be searched when a #include is found. + +SEARCH_INCLUDES = YES + +# The INCLUDE_PATH tag can be used to specify one or more directories that +# contain include files that are not input files but should be processed by +# the preprocessor. + +INCLUDE_PATH = + +# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard +# patterns (like *.h and *.hpp) to filter out the header-files in the +# directories. If left blank, the patterns specified with FILE_PATTERNS will +# be used. + +INCLUDE_FILE_PATTERNS = + +# The PREDEFINED tag can be used to specify one or more macro names that +# are defined before the preprocessor is started (similar to the -D option of +# gcc). The argument of the tag is a list of macros of the form: name +# or name=definition (no spaces). If the definition and the = are +# omitted =1 is assumed. To prevent a macro definition from being +# undefined via #undef or recursively expanded use the := operator +# instead of the = operator. + +PREDEFINED = + +# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then +# this tag can be used to specify a list of macro names that should be expanded. +# The macro definition that is found in the sources will be used. +# Use the PREDEFINED tag if you want to use a different macro definition that +# overrules the definition found in the source code. + +EXPAND_AS_DEFINED = + +# If the SKIP_FUNCTION_MACROS tag is set to YES (the default) then +# doxygen's preprocessor will remove all references to function-like macros +# that are alone on a line, have an all uppercase name, and do not end with a +# semicolon, because these will confuse the parser if not removed. + +SKIP_FUNCTION_MACROS = YES + +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- + +# The TAGFILES option can be used to specify one or more tagfiles. For each +# tag file the location of the external documentation should be added. The +# format of a tag file without this location is as follows: +# +# TAGFILES = file1 file2 ... +# Adding location for the tag files is done as follows: +# +# TAGFILES = file1=loc1 "file2 = loc2" ... +# where "loc1" and "loc2" can be relative or absolute paths +# or URLs. Note that each tag file must have a unique name (where the name does +# NOT include the path). If a tag file is not located in the directory in which +# doxygen is run, you must also specify the path to the tagfile here. + +TAGFILES = + +# When a file name is specified after GENERATE_TAGFILE, doxygen will create +# a tag file that is based on the input files it reads. + +GENERATE_TAGFILE = + +# If the ALLEXTERNALS tag is set to YES all external classes will be listed +# in the class index. If set to NO only the inherited external classes +# will be listed. + +ALLEXTERNALS = NO + +# If the EXTERNAL_GROUPS tag is set to YES all external groups will be listed +# in the modules index. If set to NO, only the current project's groups will +# be listed. + +EXTERNAL_GROUPS = YES + +# The PERL_PATH should be the absolute path and name of the perl script +# interpreter (i.e. the result of `which perl'). + +PERL_PATH = /usr/bin/perl + +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- + +# If the CLASS_DIAGRAMS tag is set to YES (the default) Doxygen will +# generate a inheritance diagram (in HTML, RTF and LaTeX) for classes with base +# or super classes. Setting the tag to NO turns the diagrams off. Note that +# this option also works with HAVE_DOT disabled, but it is recommended to +# install and use dot, since it yields more powerful graphs. + +CLASS_DIAGRAMS = YES + +# You can define message sequence charts within doxygen comments using the \msc +# command. Doxygen will then run the mscgen tool (see +# http://www.mcternan.me.uk/mscgen/) to produce the chart and insert it in the +# documentation. The MSCGEN_PATH tag allows you to specify the directory where +# the mscgen tool resides. If left empty the tool is assumed to be found in the +# default search path. + +MSCGEN_PATH = + +# If set to YES, the inheritance and collaboration graphs will hide +# inheritance and usage relations if the target is undocumented +# or is not a class. + +HIDE_UNDOC_RELATIONS = YES + +# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is +# available from the path. This tool is part of Graphviz, a graph visualization +# toolkit from AT&T and Lucent Bell Labs. The other options in this section +# have no effect if this option is set to NO (the default) + +HAVE_DOT = YES + +# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is +# allowed to run in parallel. When set to 0 (the default) doxygen will +# base this on the number of processors available in the system. You can set it +# explicitly to a value larger than 0 to get control over the balance +# between CPU load and processing speed. + +DOT_NUM_THREADS = 0 + +# By default doxygen will use the Helvetica font for all dot files that +# doxygen generates. When you want a differently looking font you can specify +# the font name using DOT_FONTNAME. You need to make sure dot is able to find +# the font, which can be done by putting it in a standard location or by setting +# the DOTFONTPATH environment variable or by setting DOT_FONTPATH to the +# directory containing the font. + +DOT_FONTNAME = Helvetica + +# The DOT_FONTSIZE tag can be used to set the size of the font of dot graphs. +# The default size is 10pt. + +DOT_FONTSIZE = 10 + +# By default doxygen will tell dot to use the Helvetica font. +# If you specify a different font using DOT_FONTNAME you can use DOT_FONTPATH to +# set the path where dot can find it. + +DOT_FONTPATH = + +# If the CLASS_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect inheritance relations. Setting this tag to YES will force the +# CLASS_DIAGRAMS tag to NO. + +CLASS_GRAPH = YES + +# If the COLLABORATION_GRAPH and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for each documented class showing the direct and +# indirect implementation dependencies (inheritance, containment, and +# class references variables) of the class with other documented classes. + +COLLABORATION_GRAPH = NO + +# If the GROUP_GRAPHS and HAVE_DOT tags are set to YES then doxygen +# will generate a graph for groups, showing the direct groups dependencies + +GROUP_GRAPHS = YES + +# If the UML_LOOK tag is set to YES doxygen will generate inheritance and +# collaboration diagrams in a style similar to the OMG's Unified Modeling +# Language. + +UML_LOOK = NO + +# If the UML_LOOK tag is enabled, the fields and methods are shown inside +# the class node. If there are many fields or methods and many nodes the +# graph may become too big to be useful. The UML_LIMIT_NUM_FIELDS +# threshold limits the number of items for each type to make the size more +# managable. Set this to 0 for no limit. Note that the threshold may be +# exceeded by 50% before the limit is enforced. + +UML_LIMIT_NUM_FIELDS = 10 + +# If set to YES, the inheritance and collaboration graphs will show the +# relations between templates and their instances. + +TEMPLATE_RELATIONS = NO + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDE_GRAPH, and HAVE_DOT +# tags are set to YES then doxygen will generate a graph for each documented +# file showing the direct and indirect include dependencies of the file with +# other documented files. + +INCLUDE_GRAPH = YES + +# If the ENABLE_PREPROCESSING, SEARCH_INCLUDES, INCLUDED_BY_GRAPH, and +# HAVE_DOT tags are set to YES then doxygen will generate a graph for each +# documented header file showing the documented files that directly or +# indirectly include this file. + +INCLUDED_BY_GRAPH = YES + +# If the CALL_GRAPH and HAVE_DOT options are set to YES then +# doxygen will generate a call dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable call graphs +# for selected functions only using the \callgraph command. + +CALL_GRAPH = NO + +# If the CALLER_GRAPH and HAVE_DOT tags are set to YES then +# doxygen will generate a caller dependency graph for every global function +# or class method. Note that enabling this option will significantly increase +# the time of a run. So in most cases it will be better to enable caller +# graphs for selected functions only using the \callergraph command. + +CALLER_GRAPH = NO + +# If the GRAPHICAL_HIERARCHY and HAVE_DOT tags are set to YES then doxygen +# will generate a graphical hierarchy of all classes instead of a textual one. + +GRAPHICAL_HIERARCHY = YES + +# If the DIRECTORY_GRAPH and HAVE_DOT tags are set to YES +# then doxygen will show the dependencies a directory has on other directories +# in a graphical way. The dependency relations are determined by the #include +# relations between the files in the directories. + +DIRECTORY_GRAPH = YES + +# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images +# generated by dot. Possible values are svg, png, jpg, or gif. +# If left blank png will be used. If you choose svg you need to set +# HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible in IE 9+ (other browsers do not have this requirement). + +DOT_IMAGE_FORMAT = png + +# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to +# enable generation of interactive SVG images that allow zooming and panning. +# Note that this requires a modern browser other than Internet Explorer. +# Tested and working are Firefox, Chrome, Safari, and Opera. For IE 9+ you +# need to set HTML_FILE_EXTENSION to xhtml in order to make the SVG files +# visible. Older versions of IE do not have SVG support. + +INTERACTIVE_SVG = NO + +# The tag DOT_PATH can be used to specify the path where the dot tool can be +# found. If left blank, it is assumed the dot tool can be found in the path. + +DOT_PATH = + +# The DOTFILE_DIRS tag can be used to specify one or more directories that +# contain dot files that are included in the documentation (see the +# \dotfile command). + +DOTFILE_DIRS = + +# The MSCFILE_DIRS tag can be used to specify one or more directories that +# contain msc files that are included in the documentation (see the +# \mscfile command). + +MSCFILE_DIRS = + +# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of +# nodes that will be shown in the graph. If the number of nodes in a graph +# becomes larger than this value, doxygen will truncate the graph, which is +# visualized by representing a node as a red box. Note that doxygen if the +# number of direct children of the root node in a graph is already larger than +# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note +# that the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH. + +DOT_GRAPH_MAX_NODES = 50 + +# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the +# graphs generated by dot. A depth value of 3 means that only nodes reachable +# from the root by following a path via at most 3 edges will be shown. Nodes +# that lay further from the root node will be omitted. Note that setting this +# option to 1 or 2 may greatly reduce the computation time needed for large +# code bases. Also note that the size of a graph can be further restricted by +# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction. + +MAX_DOT_GRAPH_DEPTH = 0 + +# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent +# background. This is disabled by default, because dot on Windows does not +# seem to support this out of the box. Warning: Depending on the platform used, +# enabling this option may lead to badly anti-aliased labels on the edges of +# a graph (i.e. they become hard to read). + +DOT_TRANSPARENT = NO + +# Set the DOT_MULTI_TARGETS tag to YES allow dot to generate multiple output +# files in one run (i.e. multiple -o and -T options on the command line). This +# makes dot run faster, but since only newer versions of dot (>1.8.10) +# support this, this feature is disabled by default. + +DOT_MULTI_TARGETS = YES + +# If the GENERATE_LEGEND tag is set to YES (the default) Doxygen will +# generate a legend page explaining the meaning of the various boxes and +# arrows in the dot generated graphs. + +GENERATE_LEGEND = YES + +# If the DOT_CLEANUP tag is set to YES (the default) Doxygen will +# remove the intermediate dot files that are used to generate +# the various graphs. + +DOT_CLEANUP = YES
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Resources/SyncOrthancFolder.py Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,118 @@ +#!/usr/bin/python + +# +# This maintenance script updates the content of the "Orthanc" folder +# to match the latest version of the Orthanc source code. +# + +import sys +import multiprocessing +import os +import stat +import urllib2 + +TARGET = os.path.join(os.path.dirname(__file__), '../Framework/Orthanc') +REPOSITORY = 'https://bitbucket.org/sjodogne/orthanc/raw' + +FILES = [ + 'Core/ChunkedBuffer.cpp', + 'Core/ChunkedBuffer.h', + 'Core/Compression/DeflateBaseCompressor.cpp', + 'Core/Compression/DeflateBaseCompressor.h', + 'Core/Compression/GzipCompressor.cpp', + 'Core/Compression/GzipCompressor.h', + 'Core/Compression/IBufferCompressor.h', + 'Core/Enumerations.cpp', + 'Core/Enumerations.h', + 'Core/HttpClient.cpp', + 'Core/HttpClient.h', + 'Core/Images/Image.cpp', + 'Core/Images/Image.h', + 'Core/Images/ImageAccessor.cpp', + 'Core/Images/ImageAccessor.h', + 'Core/Images/ImageBuffer.cpp', + 'Core/Images/ImageBuffer.h', + 'Core/Images/ImageProcessing.cpp', + 'Core/Images/ImageProcessing.h', + 'Core/Images/JpegErrorManager.cpp', + 'Core/Images/JpegErrorManager.h', + 'Core/Images/JpegReader.cpp', + 'Core/Images/JpegReader.h', + 'Core/Images/PngReader.cpp', + 'Core/Images/PngReader.h', + 'Core/Logging.cpp', + 'Core/Logging.h', + 'Core/OrthancException.h', + 'Core/PrecompiledHeaders.h', + 'Core/Toolbox.cpp', + 'Core/Toolbox.h', + 'Core/WebServiceParameters.cpp', + 'Core/WebServiceParameters.h', + 'Resources/CMake/AutoGeneratedCode.cmake', + 'Resources/CMake/BoostConfiguration.cmake', + 'Resources/CMake/Compiler.cmake', + 'Resources/CMake/DownloadPackage.cmake', + 'Resources/CMake/JsonCppConfiguration.cmake', + 'Resources/CMake/LibCurlConfiguration.cmake', + 'Resources/CMake/LibIconvConfiguration.cmake', + 'Resources/CMake/LibJpegConfiguration.cmake', + 'Resources/CMake/LibPngConfiguration.cmake', + 'Resources/CMake/OpenSslConfiguration.cmake', + 'Resources/CMake/ZlibConfiguration.cmake', + 'Resources/EmbedResources.py', + 'Resources/MinGW-W64-Toolchain32.cmake', + 'Resources/MinGW-W64-Toolchain64.cmake', + 'Resources/MinGWToolchain.cmake', + 'Resources/ThirdParty/VisualStudio/stdint.h', + 'Resources/ThirdParty/base64/base64.cpp', + 'Resources/ThirdParty/base64/base64.h', + 'Resources/ThirdParty/patch/msys-1.0.dll', + 'Resources/ThirdParty/patch/patch.exe', + 'Resources/ThirdParty/patch/patch.exe.manifest', + 'Resources/WindowsResources.py', + 'Resources/WindowsResources.rc', +] + +EXE = [ + 'Resources/WindowsResources.py', +] + + +def Download(x): + branch = x[0] + source = x[1] + target = os.path.join(TARGET, x[2]) + print target + + try: + os.makedirs(os.path.dirname(target)) + except: + pass + + url = '%s/%s/%s' % (REPOSITORY, branch, source) + + with open(target, 'w') as f: + f.write(urllib2.urlopen(url).read()) + + +commands = [] + +for f in FILES: + commands.append([ 'default', f, f ]) + + +if sys.platform == 'win32': + # Sequential execution + for c in commands: + Download(c) +else: + # Concurrent downloads + pool = multiprocessing.Pool(10) + pool.map(Download, commands) + + +for exe in EXE: + path = os.path.join(TARGET, exe) + st = os.stat(path) + os.chmod(path, st.st_mode | stat.S_IEXEC) +
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/BasicPetCtFusionApplication.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,213 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleInteractor.h" + +#include "../Framework/Orthanc/Core/Logging.h" + +namespace OrthancStone +{ + namespace Samples + { + class BasicPetCtFusionApplication : public SampleApplicationBase + { + private: + class Interactor : public SampleInteractor + { + public: + static void SetStyle(LayeredSceneWidget& widget, + bool ct, + bool pet) + { + if (ct) + { + RenderStyle style; + style.windowing_ = ImageWindowing_Bone; + widget.SetLayerStyle(0, style); + } + else + { + RenderStyle style; + style.visible_ = false; + widget.SetLayerStyle(0, style); + } + + if (ct && pet) + { + RenderStyle style; + style.applyLut_ = true; + style.alpha_ = 0.5; + widget.SetLayerStyle(1, style); + } + else if (pet) + { + RenderStyle style; + style.applyLut_ = true; + widget.SetLayerStyle(1, style); + } + else + { + RenderStyle style; + style.visible_ = false; + widget.SetLayerStyle(1, style); + } + } + + + static bool IsVisible(LayeredSceneWidget& widget, + size_t layer) + { + RenderStyle style = widget.GetLayerStyle(layer); + return style.visible_; + } + + + static void ToggleInterpolation(LayeredSceneWidget& widget, + size_t layer) + { + RenderStyle style = widget.GetLayerStyle(layer); + + if (style.interpolation_ == ImageInterpolation_Linear) + { + style.interpolation_ = ImageInterpolation_Nearest; + } + else + { + style.interpolation_ = ImageInterpolation_Linear; + } + + widget.SetLayerStyle(layer, style); + } + + + Interactor(VolumeImage& volume, + VolumeProjection projection, + bool reverse) : + SampleInteractor(volume, projection, reverse) + { + } + + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + LayeredSceneWidget& layered = dynamic_cast<LayeredSceneWidget&>(widget); + + switch (key) + { + case 'c': + // Toggle the visibility of the CT layer + SetStyle(layered, !IsVisible(layered, 0), IsVisible(layered, 1)); + break; + + case 'p': + // Toggle the visibility of the PET layer + SetStyle(layered, IsVisible(layered, 0), !IsVisible(layered, 1)); + break; + + case 'i': + { + // Toggle on/off the interpolation + ToggleInterpolation(layered, 0); + ToggleInterpolation(layered, 1); + break; + } + + default: + break; + } + } + }; + + + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("ct", boost::program_options::value<std::string>(), + "Orthanc ID of the CT series") + ("pet", boost::program_options::value<std::string>(), + "Orthanc ID of the PET series") + ("threads", boost::program_options::value<unsigned int>()->default_value(3), + "Number of download threads for the CT series") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + if (parameters.count("ct") != 1 || + parameters.count("pet") != 1) + { + LOG(ERROR) << "The series ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string ct = parameters["ct"].as<std::string>(); + std::string pet = parameters["pet"].as<std::string>(); + unsigned int threads = parameters["threads"].as<unsigned int>(); + + VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads); + VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1); + + // Take the PET volume as the reference for the slices + std::auto_ptr<Interactor> interactor(new Interactor(petVolume, VolumeProjection_Axial, false /* don't reverse normal */)); + + std::auto_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget); + widget->AddLayer(new VolumeImage::LayerFactory(ctVolume)); + widget->AddLayer(new VolumeImage::LayerFactory(petVolume)); + widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); + widget->SetInteractor(*interactor); + + Interactor::SetStyle(*widget, true, true); // Initially, show both CT and PET layers + + context.AddInteractor(interactor.release()); + context.SetCentralWidget(widget.release()); + + statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode"); + statusBar.SetMessage("Use the key \"c\" to show/hide the CT layer"); + statusBar.SetMessage("Use the key \"p\" to show/hide the PET layer"); + statusBar.SetMessage("Use the key \"i\" to toggle the smoothing of the images"); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/EmptyApplication.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,70 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleApplicationBase.h" + +#include "../Framework/Widgets/EmptyWidget.h" + +namespace OrthancStone +{ + namespace Samples + { + class EmptyApplication : public SampleApplicationBase + { + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("red", boost::program_options::value<int>()->default_value(255), "Background color: red channel") + ("green", boost::program_options::value<int>()->default_value(0), "Background color: green channel") + ("blue", boost::program_options::value<int>()->default_value(0), "Background color: blue channel") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + int red = parameters["red"].as<int>(); + int green = parameters["green"].as<int>(); + int blue = parameters["blue"].as<int>(); + + context.SetCentralWidget(new EmptyWidget(red, green, blue)); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/LayoutPetCtFusionApplication.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,409 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleInteractor.h" + +#include "../Framework/Layers/SiblingSliceLocationFactory.h" +#include "../Framework/Layers/DicomStructureSetRendererFactory.h" +#include "../Framework/Widgets/LayoutWidget.h" + +#include "../Framework/Orthanc/Core/Logging.h" + +namespace OrthancStone +{ + namespace Samples + { + class LayoutPetCtFusionApplication : + public SampleApplicationBase, + public LayeredSceneWidget::ISliceObserver, + public WorldSceneWidget::IWorldObserver + { + private: + class Interactor : public SampleInteractor + { + private: + LayoutPetCtFusionApplication& that_; + + public: + Interactor(LayoutPetCtFusionApplication& that, + VolumeImage& volume, + VolumeProjection projection, + bool reverse) : + SampleInteractor(volume, projection, reverse), + that_(that) + { + } + + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + MouseButton button, + double x, + double y, + IStatusBar* statusBar) + { + if (button == MouseButton_Left) + { + // Center the sibling views over the clicked point + Vector p = slice.MapSliceToWorldCoordinates(x, y); + + if (statusBar != NULL) + { + char buf[64]; + sprintf(buf, "Click on coordinates (%.02f,%.02f,%.02f) in cm", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); + statusBar->SetMessage(buf); + } + + that_.interactorAxial_->LookupSliceContainingPoint(*that_.ctAxial_, p); + that_.interactorCoronal_->LookupSliceContainingPoint(*that_.ctCoronal_, p); + that_.interactorSagittal_->LookupSliceContainingPoint(*that_.ctSagittal_, p); + } + + return NULL; + } + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + if (key == 's') + { + that_.SetDefaultView(); + } + } + }; + + bool processingEvent_; + Interactor* interactorAxial_; + Interactor* interactorCoronal_; + Interactor* interactorSagittal_; + LayeredSceneWidget* ctAxial_; + LayeredSceneWidget* ctCoronal_; + LayeredSceneWidget* ctSagittal_; + LayeredSceneWidget* petAxial_; + LayeredSceneWidget* petCoronal_; + LayeredSceneWidget* petSagittal_; + LayeredSceneWidget* fusionAxial_; + LayeredSceneWidget* fusionCoronal_; + LayeredSceneWidget* fusionSagittal_; + + + void SetDefaultView() + { + petAxial_->SetDefaultView(); + petCoronal_->SetDefaultView(); + petSagittal_->SetDefaultView(); + } + + + void AddLayer(LayeredSceneWidget& widget, + VolumeImage& volume, + bool isCt) + { + size_t layer; + widget.AddLayer(layer, new VolumeImage::LayerFactory(volume)); + + if (isCt) + { + RenderStyle style; + style.windowing_ = ImageWindowing_Bone; + widget.SetLayerStyle(layer, style); + } + else + { + RenderStyle style; + style.applyLut_ = true; + style.alpha_ = (layer == 0 ? 1.0 : 0.5); + widget.SetLayerStyle(layer, style); + } + } + + + void ConnectSiblingLocations(LayeredSceneWidget& axial, + LayeredSceneWidget& coronal, + LayeredSceneWidget& sagittal) + { + SiblingSliceLocationFactory::Configure(axial, coronal); + SiblingSliceLocationFactory::Configure(axial, sagittal); + SiblingSliceLocationFactory::Configure(coronal, sagittal); + } + + + void SynchronizeView(const WorldSceneWidget& source, + const ViewportGeometry& view, + LayeredSceneWidget& widget1, + LayeredSceneWidget& widget2, + LayeredSceneWidget& widget3) + { + if (&source == &widget1 || + &source == &widget2 || + &source == &widget3) + { + if (&source != &widget1) + { + widget1.SetView(view); + } + + if (&source != &widget2) + { + widget2.SetView(view); + } + + if (&source != &widget3) + { + widget3.SetView(view); + } + } + } + + + void SynchronizeSlice(const LayeredSceneWidget& source, + const SliceGeometry& slice, + LayeredSceneWidget& widget1, + LayeredSceneWidget& widget2, + LayeredSceneWidget& widget3) + { + if (&source == &widget1 || + &source == &widget2 || + &source == &widget3) + { + if (&source != &widget1) + { + widget1.SetSlice(slice); + } + + if (&source != &widget2) + { + widget2.SetSlice(slice); + } + + if (&source != &widget3) + { + widget3.SetSlice(slice); + } + } + } + + + LayeredSceneWidget* CreateWidget() + { + std::auto_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget); + widget->Register(dynamic_cast<WorldSceneWidget::IWorldObserver&>(*this)); + widget->Register(dynamic_cast<LayeredSceneWidget::ISliceObserver&>(*this)); + return widget.release(); + } + + + void CreateLayout(BasicApplicationContext& context) + { + std::auto_ptr<OrthancStone::LayoutWidget> layout(new OrthancStone::LayoutWidget); + layout->SetBackgroundCleared(true); + //layout->SetBackgroundColor(255,0,0); + layout->SetPadding(5); + + OrthancStone::LayoutWidget& layoutA = dynamic_cast<OrthancStone::LayoutWidget&> + (layout->AddWidget(new OrthancStone::LayoutWidget)); + layoutA.SetPadding(0, 0, 0, 0, 5); + layoutA.SetVertical(); + petAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutA.AddWidget(CreateWidget())); + OrthancStone::LayoutWidget& layoutA2 = dynamic_cast<OrthancStone::LayoutWidget&> + (layoutA.AddWidget(new OrthancStone::LayoutWidget)); + layoutA2.SetPadding(0, 0, 0, 0, 5); + petSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutA2.AddWidget(CreateWidget())); + petCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutA2.AddWidget(CreateWidget())); + + OrthancStone::LayoutWidget& layoutB = dynamic_cast<OrthancStone::LayoutWidget&> + (layout->AddWidget(new OrthancStone::LayoutWidget)); + layoutB.SetPadding(0, 0, 0, 0, 5); + layoutB.SetVertical(); + ctAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutB.AddWidget(CreateWidget())); + OrthancStone::LayoutWidget& layoutB2 = dynamic_cast<OrthancStone::LayoutWidget&> + (layoutB.AddWidget(new OrthancStone::LayoutWidget)); + layoutB2.SetPadding(0, 0, 0, 0, 5); + ctSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutB2.AddWidget(CreateWidget())); + ctCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutB2.AddWidget(CreateWidget())); + + OrthancStone::LayoutWidget& layoutC = dynamic_cast<OrthancStone::LayoutWidget&> + (layout->AddWidget(new OrthancStone::LayoutWidget)); + layoutC.SetPadding(0, 0, 0, 0, 5); + layoutC.SetVertical(); + fusionAxial_ = &dynamic_cast<LayeredSceneWidget&>(layoutC.AddWidget(CreateWidget())); + OrthancStone::LayoutWidget& layoutC2 = dynamic_cast<OrthancStone::LayoutWidget&> + (layoutC.AddWidget(new OrthancStone::LayoutWidget)); + layoutC2.SetPadding(0, 0, 0, 0, 5); + fusionSagittal_ = &dynamic_cast<LayeredSceneWidget&>(layoutC2.AddWidget(CreateWidget())); + fusionCoronal_ = &dynamic_cast<LayeredSceneWidget&>(layoutC2.AddWidget(CreateWidget())); + + context.SetCentralWidget(layout.release()); + } + + + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("ct", boost::program_options::value<std::string>(), + "Orthanc ID of the CT series") + ("pet", boost::program_options::value<std::string>(), + "Orthanc ID of the PET series") + ("rt", boost::program_options::value<std::string>(), + "Orthanc ID of the DICOM RT-STRUCT series (optional)") + ("threads", boost::program_options::value<unsigned int>()->default_value(3), + "Number of download threads for the CT series") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + processingEvent_ = true; + + if (parameters.count("ct") != 1 || + parameters.count("pet") != 1) + { + LOG(ERROR) << "The series ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string ct = parameters["ct"].as<std::string>(); + std::string pet = parameters["pet"].as<std::string>(); + unsigned int threads = parameters["threads"].as<unsigned int>(); + + VolumeImage& ctVolume = context.AddSeriesVolume(ct, true /* progressive download */, threads); + VolumeImage& petVolume = context.AddSeriesVolume(pet, true /* progressive download */, 1); + + // Take the PET volume as the reference for the slices + interactorAxial_ = &dynamic_cast<Interactor&> + (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Axial, false))); + interactorCoronal_ = &dynamic_cast<Interactor&> + (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Coronal, false))); + interactorSagittal_ = &dynamic_cast<Interactor&> + (context.AddInteractor(new Interactor(*this, petVolume, VolumeProjection_Sagittal, true))); + + CreateLayout(context); + + AddLayer(*ctAxial_, ctVolume, true); + AddLayer(*ctCoronal_, ctVolume, true); + AddLayer(*ctSagittal_, ctVolume, true); + + AddLayer(*petAxial_, petVolume, false); + AddLayer(*petCoronal_, petVolume, false); + AddLayer(*petSagittal_, petVolume, false); + + AddLayer(*fusionAxial_, ctVolume, true); + AddLayer(*fusionAxial_, petVolume, false); + AddLayer(*fusionCoronal_, ctVolume, true); + AddLayer(*fusionCoronal_, petVolume, false); + AddLayer(*fusionSagittal_, ctVolume, true); + AddLayer(*fusionSagittal_, petVolume, false); + + if (parameters.count("rt") == 1) + { + DicomStructureSet& rtStruct = context.AddStructureSet(parameters["rt"].as<std::string>()); + + Vector p = rtStruct.GetStructureCenter(0); + interactorAxial_->GetCursor().LookupSliceContainingPoint(p); + + ctAxial_->AddLayer(new DicomStructureSetRendererFactory(rtStruct)); + petAxial_->AddLayer(new DicomStructureSetRendererFactory(rtStruct)); + fusionAxial_->AddLayer(new DicomStructureSetRendererFactory(rtStruct)); + } + + ConnectSiblingLocations(*ctAxial_, *ctCoronal_, *ctSagittal_); + ConnectSiblingLocations(*petAxial_, *petCoronal_, *petSagittal_); + ConnectSiblingLocations(*fusionAxial_, *fusionCoronal_, *fusionSagittal_); + + interactorAxial_->AddWidget(*ctAxial_); + interactorAxial_->AddWidget(*petAxial_); + interactorAxial_->AddWidget(*fusionAxial_); + + interactorCoronal_->AddWidget(*ctCoronal_); + interactorCoronal_->AddWidget(*petCoronal_); + interactorCoronal_->AddWidget(*fusionCoronal_); + + interactorSagittal_->AddWidget(*ctSagittal_); + interactorSagittal_->AddWidget(*petSagittal_); + interactorSagittal_->AddWidget(*fusionSagittal_); + + processingEvent_ = false; + + statusBar.SetMessage("Use the key \"t\" to toggle the fullscreen mode"); + statusBar.SetMessage("Use the key \"s\" to reinitialize the layout"); + } + + virtual void NotifySizeChange(const WorldSceneWidget& source, + ViewportGeometry& view) + { + view.SetDefaultView(); + } + + virtual void NotifyViewChange(const WorldSceneWidget& source, + const ViewportGeometry& view) + { + if (!processingEvent_) // Avoid reentrant calls + { + processingEvent_ = true; + + SynchronizeView(source, view, *ctAxial_, *petAxial_, *fusionAxial_); + SynchronizeView(source, view, *ctCoronal_, *petCoronal_, *fusionCoronal_); + SynchronizeView(source, view, *ctSagittal_, *petSagittal_, *fusionSagittal_); + + processingEvent_ = false; + } + } + + virtual void NotifySliceChange(const LayeredSceneWidget& source, + const SliceGeometry& slice) + { + if (!processingEvent_) // Avoid reentrant calls + { + processingEvent_ = true; + + SynchronizeSlice(source, slice, *ctAxial_, *petAxial_, *fusionAxial_); + SynchronizeSlice(source, slice, *ctCoronal_, *petCoronal_, *fusionCoronal_); + SynchronizeSlice(source, slice, *ctSagittal_, *petSagittal_, *fusionSagittal_); + + processingEvent_ = false; + } + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/SampleApplicationBase.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,58 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "../Framework/Applications/IBasicApplication.h" + +namespace OrthancStone +{ + namespace Samples + { + class SampleApplicationBase : public IBasicApplication + { + public: + virtual std::string GetTitle() const + { + return "Stone of Orthanc - Sample"; + } + + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + } + + virtual void Finalize() + { + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/SampleInteractor.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,144 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleApplicationBase.h" + +#include "../Framework/Widgets/LayeredSceneWidget.h" +#include "../Framework/Widgets/IWorldSceneInteractor.h" +#include "../Framework/Toolbox/ParallelSlicesCursor.h" + +namespace OrthancStone +{ + namespace Samples + { + /** + * This is a basic mouse interactor for sample applications. It + * contains a set of parallel slices in the 3D space. The mouse + * wheel events make the widget change the slice that is + * displayed. + **/ + class SampleInteractor : public IWorldSceneInteractor + { + private: + ParallelSlicesCursor cursor_; + + public: + SampleInteractor(VolumeImage& volume, + VolumeProjection projection, + bool reverse) + { + std::auto_ptr<ParallelSlices> slices(volume.GetGeometry(projection, reverse)); + cursor_.SetGeometry(*slices); + } + + SampleInteractor(ISeriesLoader& series, + bool reverse) + { + if (reverse) + { + std::auto_ptr<ParallelSlices> slices(series.GetGeometry().Reverse()); + cursor_.SetGeometry(*slices); + } + else + { + cursor_.SetGeometry(series.GetGeometry()); + } + } + + SampleInteractor(const ParallelSlices& slices) + { + cursor_.SetGeometry(slices); + } + + ParallelSlicesCursor& GetCursor() + { + return cursor_; + } + + void AddWidget(LayeredSceneWidget& widget) + { + widget.SetInteractor(*this); + widget.SetSlice(cursor_.GetCurrentSlice()); + } + + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + MouseButton button, + double x, + double y, + IStatusBar* statusBar) + { + return NULL; + } + + virtual void MouseOver(CairoContext& context, + WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) + { + } + + virtual void MouseWheel(WorldSceneWidget& widget, + MouseWheelDirection direction, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + if (cursor_.ApplyWheelEvent(direction, modifiers)) + { + dynamic_cast<LayeredSceneWidget&>(widget).SetSlice(cursor_.GetCurrentSlice()); + } + } + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + } + + void LookupSliceContainingPoint(LayeredSceneWidget& widget, + const Vector& p) + { + if (cursor_.LookupSliceContainingPoint(p)) + { + widget.SetSlice(cursor_.GetCurrentSlice()); + } + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/SampleMainSdl.cpp Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,73 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +// The macro "ORTHANC_STONE_SAMPLE" must be set by the CMake script + +#if ORTHANC_STONE_SAMPLE == 1 +#include "../../Samples/EmptyApplication.h" +typedef OrthancStone::Samples::EmptyApplication Application; + +#elif ORTHANC_STONE_SAMPLE == 2 +#include "../../Samples/TestPatternApplication.h" +typedef OrthancStone::Samples::TestPatternApplication Application; + +#elif ORTHANC_STONE_SAMPLE == 3 +#include "../../Samples/SingleFrameApplication.h" +typedef OrthancStone::Samples::SingleFrameApplication Application; + +#elif ORTHANC_STONE_SAMPLE == 4 +#include "../../Samples/SingleVolumeApplication.h" +typedef OrthancStone::Samples::SingleVolumeApplication Application; + +#elif ORTHANC_STONE_SAMPLE == 5 +#include "../../Samples/BasicPetCtFusionApplication.h" +typedef OrthancStone::Samples::BasicPetCtFusionApplication Application; + +#elif ORTHANC_STONE_SAMPLE == 6 +#include "../../Samples/SynchronizedSeriesApplication.h" +typedef OrthancStone::Samples::SynchronizedSeriesApplication Application; + +#elif ORTHANC_STONE_SAMPLE == 7 +#include "../../Samples/LayoutPetCtFusionApplication.h" +typedef OrthancStone::Samples::LayoutPetCtFusionApplication Application; + +#else +#error Please set the ORTHANC_STONE_SAMPLE macro +#endif + + +int main(int argc, char* argv[]) +{ + Application application; + + return OrthancStone::IBasicApplication::ExecuteWithSdl(application, argc, argv); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/SingleFrameApplication.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,96 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleApplicationBase.h" + +#include "../Framework/Layers/SingleFrameRendererFactory.h" +#include "../Framework/Widgets/LayeredSceneWidget.h" +#include "../Framework/Orthanc/Core/Logging.h" + +namespace OrthancStone +{ + namespace Samples + { + class SingleFrameApplication : public SampleApplicationBase + { + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("instance", boost::program_options::value<std::string>(), + "Orthanc ID of the instance") + ("frame", boost::program_options::value<unsigned int>()->default_value(0), + "Number of the frame, for multi-frame DICOM instances") + ("smooth", boost::program_options::value<bool>()->default_value(true), + "Enable linear interpolation to smooth the image") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + if (parameters.count("instance") != 1) + { + LOG(ERROR) << "The instance ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string instance = parameters["instance"].as<std::string>(); + int frame = parameters["frame"].as<unsigned int>(); + + std::auto_ptr<SingleFrameRendererFactory> renderer; + renderer.reset(new SingleFrameRendererFactory(context.GetOrthancConnection(), instance, frame)); + + std::auto_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget); + widget->SetSlice(renderer->GetSliceGeometry()); + widget->AddLayer(renderer.release()); + + if (parameters["smooth"].as<bool>()) + { + RenderStyle s; + s.interpolation_ = ImageInterpolation_Linear; + widget->SetLayerStyle(0, s); + } + + context.SetCentralWidget(widget.release()); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/SingleVolumeApplication.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,295 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleInteractor.h" + +#include "../Framework/Orthanc/Core/Toolbox.h" +#include "../Framework/Layers/LineMeasureTracker.h" +#include "../Framework/Layers/CircleMeasureTracker.h" +#include "../Framework/Orthanc/Core/Logging.h" + +namespace OrthancStone +{ + namespace Samples + { + class SingleVolumeApplication : public SampleApplicationBase + { + private: + class Interactor : public SampleInteractor + { + private: + enum MouseMode + { + MouseMode_None, + MouseMode_TrackCoordinates, + MouseMode_LineMeasure, + MouseMode_CircleMeasure + }; + + MouseMode mouseMode_; + + void SetMouseMode(MouseMode mode, + IStatusBar* statusBar) + { + if (mouseMode_ == mode) + { + mouseMode_ = MouseMode_None; + } + else + { + mouseMode_ = mode; + } + + if (statusBar) + { + switch (mouseMode_) + { + case MouseMode_None: + statusBar->SetMessage("Disabling the mouse tools"); + break; + + case MouseMode_TrackCoordinates: + statusBar->SetMessage("Tracking the mouse coordinates"); + break; + + case MouseMode_LineMeasure: + statusBar->SetMessage("Mouse clicks will now measure the distances"); + break; + + case MouseMode_CircleMeasure: + statusBar->SetMessage("Mouse clicks will now draw circles"); + break; + + default: + break; + } + } + } + + public: + Interactor(VolumeImage& volume, + VolumeProjection projection, + bool reverse) : + SampleInteractor(volume, projection, reverse), + mouseMode_(MouseMode_None) + { + } + + virtual IWorldSceneMouseTracker* CreateMouseTracker(WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + MouseButton button, + double x, + double y, + IStatusBar* statusBar) + { + if (button == MouseButton_Left) + { + switch (mouseMode_) + { + case MouseMode_LineMeasure: + return new LineMeasureTracker(NULL, slice, x, y, 255, 0, 0, 14 /* font size */); + + case MouseMode_CircleMeasure: + return new CircleMeasureTracker(NULL, slice, x, y, 255, 0, 0, 14 /* font size */); + + default: + break; + } + } + + return NULL; + } + + virtual void MouseOver(CairoContext& context, + WorldSceneWidget& widget, + const SliceGeometry& slice, + const ViewportGeometry& view, + double x, + double y, + IStatusBar* statusBar) + { + if (mouseMode_ == MouseMode_TrackCoordinates && + statusBar != NULL) + { + Vector p = slice.MapSliceToWorldCoordinates(x, y); + + char buf[64]; + sprintf(buf, "X = %.02f Y = %.02f Z = %.02f (in cm)", p[0] / 10.0, p[1] / 10.0, p[2] / 10.0); + statusBar->SetMessage(buf); + } + } + + + virtual void KeyPressed(WorldSceneWidget& widget, + char key, + KeyboardModifiers modifiers, + IStatusBar* statusBar) + { + switch (key) + { + case 't': + SetMouseMode(MouseMode_TrackCoordinates, statusBar); + break; + + case 'm': + SetMouseMode(MouseMode_LineMeasure, statusBar); + break; + + case 'c': + SetMouseMode(MouseMode_CircleMeasure, statusBar); + break; + + case 'b': + { + if (statusBar) + { + statusBar->SetMessage("Setting Hounsfield window to bones"); + } + + RenderStyle style; + style.windowing_ = ImageWindowing_Bone; + dynamic_cast<LayeredSceneWidget&>(widget).SetLayerStyle(0, style); + break; + } + + case 'l': + { + if (statusBar) + { + statusBar->SetMessage("Setting Hounsfield window to lung"); + } + + RenderStyle style; + style.windowing_ = ImageWindowing_Lung; + dynamic_cast<LayeredSceneWidget&>(widget).SetLayerStyle(0, style); + break; + } + + case 'd': + { + if (statusBar) + { + statusBar->SetMessage("Setting Hounsfield window to what is written in the DICOM file"); + } + + RenderStyle style; + style.windowing_ = ImageWindowing_Default; + dynamic_cast<LayeredSceneWidget&>(widget).SetLayerStyle(0, style); + break; + } + + default: + break; + } + } + }; + + + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("series", boost::program_options::value<std::string>(), + "Orthanc ID of the series") + ("threads", boost::program_options::value<unsigned int>()->default_value(3), + "Number of download threads") + ("projection", boost::program_options::value<std::string>()->default_value("axial"), + "Projection of interest (can be axial, sagittal or coronal)") + ("reverse", boost::program_options::value<bool>()->default_value(false), + "Reverse the normal direction of the volume") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + if (parameters.count("series") != 1) + { + LOG(ERROR) << "The series ID is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::string series = parameters["series"].as<std::string>(); + unsigned int threads = parameters["threads"].as<unsigned int>(); + bool reverse = parameters["reverse"].as<bool>(); + + std::string tmp = parameters["projection"].as<std::string>(); + Orthanc::Toolbox::ToLowerCase(tmp); + + VolumeProjection projection; + if (tmp == "axial") + { + projection = VolumeProjection_Axial; + } + else if (tmp == "sagittal") + { + projection = VolumeProjection_Sagittal; + } + else if (tmp == "coronal") + { + projection = VolumeProjection_Coronal; + } + else + { + LOG(ERROR) << "Unknown projection: " << tmp; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + VolumeImage& volume = context.AddSeriesVolume(series, true /* progressive download */, threads); + + std::auto_ptr<Interactor> interactor(new Interactor(volume, projection, reverse)); + + std::auto_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget); + widget->AddLayer(new VolumeImage::LayerFactory(volume)); + widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); + widget->SetInteractor(*interactor); + + context.AddInteractor(interactor.release()); + context.SetCentralWidget(widget.release()); + + statusBar.SetMessage("Use the keys \"b\", \"l\" and \"d\" to change Hounsfield windowing"); + statusBar.SetMessage("Use the keys \"t\" to track the (X,Y,Z) mouse coordinates"); + statusBar.SetMessage("Use the keys \"m\" to measure distances"); + statusBar.SetMessage("Use the keys \"c\" to draw circles"); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/SynchronizedSeriesApplication.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,118 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleInteractor.h" + +#include "../Framework/Toolbox/OrthancSeriesLoader.h" +#include "../Framework/Layers/SeriesFrameRendererFactory.h" +#include "../Framework/Layers/SiblingSliceLocationFactory.h" +#include "../Framework/Widgets/LayoutWidget.h" +#include "../Framework/Orthanc/Core/Logging.h" + +namespace OrthancStone +{ + namespace Samples + { + class SynchronizedSeriesApplication : public SampleApplicationBase + { + private: + LayeredSceneWidget* CreateSeriesWidget(BasicApplicationContext& context, + const std::string& series) + { + std::auto_ptr<ISeriesLoader> loader(new OrthancSeriesLoader(context.GetOrthancConnection(), series)); + + std::auto_ptr<SampleInteractor> interactor(new SampleInteractor(*loader, false)); + + std::auto_ptr<LayeredSceneWidget> widget(new LayeredSceneWidget); + widget->AddLayer(new SeriesFrameRendererFactory(loader.release(), false)); + widget->SetSlice(interactor->GetCursor().GetCurrentSlice()); + widget->SetInteractor(*interactor); + + context.AddInteractor(interactor.release()); + + return widget.release(); + } + + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("a", boost::program_options::value<std::string>(), + "Orthanc ID of the 1st series") + ("b", boost::program_options::value<std::string>(), + "Orthanc ID of the 2nd series") + ("c", boost::program_options::value<std::string>(), + "Orthanc ID of the 3rd series") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + if (parameters.count("a") != 1 || + parameters.count("b") != 1 || + parameters.count("c") != 1) + { + LOG(ERROR) << "At least one of the three series IDs is missing"; + throw Orthanc::OrthancException(Orthanc::ErrorCode_ParameterOutOfRange); + } + + std::auto_ptr<LayeredSceneWidget> a(CreateSeriesWidget(context, parameters["a"].as<std::string>())); + std::auto_ptr<LayeredSceneWidget> b(CreateSeriesWidget(context, parameters["b"].as<std::string>())); + std::auto_ptr<LayeredSceneWidget> c(CreateSeriesWidget(context, parameters["c"].as<std::string>())); + + SiblingSliceLocationFactory::Configure(*a, *b); + SiblingSliceLocationFactory::Configure(*a, *c); + SiblingSliceLocationFactory::Configure(*b, *c); + + std::auto_ptr<LayoutWidget> layout(new LayoutWidget); + layout->SetPadding(5); + layout->AddWidget(a.release()); + + std::auto_ptr<LayoutWidget> layoutB(new LayoutWidget); + layoutB->SetVertical(); + layoutB->SetPadding(5); + layoutB->AddWidget(b.release()); + layoutB->AddWidget(c.release()); + layout->AddWidget(layoutB.release()); + + context.SetCentralWidget(layout.release()); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Samples/TestPatternApplication.h Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,74 @@ +/** + * Stone of Orthanc + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * In addition, as a special exception, the copyright holders of this + * program give permission to link the code of its release with the + * OpenSSL project's "OpenSSL" library (or with modified versions of it + * that use the same license as the "OpenSSL" library), and distribute + * the linked executables. You must obey the GNU General Public License + * in all respects for all of the code used other than "OpenSSL". If you + * modify file(s) with this exception, you may extend this exception to + * your version of the file(s), but you are not obligated to do so. If + * you do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source files + * in the program, then also delete it here. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#pragma once + +#include "SampleApplicationBase.h" + +#include "../Framework/Widgets/TestCairoWidget.h" +#include "../Framework/Widgets/TestWorldSceneWidget.h" +#include "../Framework/Widgets/LayoutWidget.h" + +namespace OrthancStone +{ + namespace Samples + { + class TestPatternApplication : public SampleApplicationBase + { + public: + virtual void DeclareCommandLineOptions(boost::program_options::options_description& options) + { + boost::program_options::options_description generic("Sample options"); + generic.add_options() + ("animate", boost::program_options::value<bool>()->default_value(true), "Animate the test pattern") + ; + + options.add(generic); + } + + virtual void Initialize(BasicApplicationContext& context, + IStatusBar& statusBar, + const boost::program_options::variables_map& parameters) + { + using namespace OrthancStone; + + std::auto_ptr<LayoutWidget> layout(new LayoutWidget); + layout->SetPadding(10); + layout->SetBackgroundCleared(true); + layout->AddWidget(new TestCairoWidget(parameters["animate"].as<bool>())); + layout->AddWidget(new TestWorldSceneWidget); + + context.SetCentralWidget(layout.release()); + } + }; + } +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TODO Fri Oct 14 15:34:11 2016 +0200 @@ -0,0 +1,57 @@ +Stone of Orthanc +================ + + +------- +General +------- + +* Documentation +* LayoutPetCtFusionApplication: fix initial view + + +--------------------------------- +Radiotherapy and nuclear medicine +--------------------------------- + +* Project RT-STRUCT in sagittal + coronal views +* Speedup RT-STRUCT loading +* Automatic segmentation + manual contouring +* Display segments in mask + + +------------- +Optimizations +------------- + +* Tune number of loading threads in LayeredSceneWidget +* Add cache over IOrthancServices (for SDL/Qt/...) +* LayoutWidget: Do not update full background if only 1 widget has changed +* LayoutWidget: Threads to refresh each child +* Implement binary search to speed up search for closest slice +* Avoid the creation of new threads when updating the frame factory + (as seen with gdb) + + +----------------- +Platform-specific +----------------- + +* Qt widget example +* Add precompiled headers for Microsoft Visual Studio +* Investigate crash in CurlOrthancConnection if using MinGW32 in Release mode + + +--------------------- +Source code cosmetics +--------------------- + +* Remove #include "OrthancException.h" in "ObserversRegistry.h" +* Use "SampleInteractor::AddWidget()" in all samples + + +------ +Future +------ + +* Create a wrapper for Python